Java-面向对象-特性-多态

121 阅读4分钟

什么是多态?

public class Person {
	String name;
	int age;
	
	public void eat(){
		System.out.println("人,吃饭");
	}
	
	public void walk(){
		System.out.println("人,走路");
	}
	
}
public class Woman extends Person{
``
	boolean isBeauty;
	
	public void goShopping(){
		System.out.println("女人喜欢购物");
	}
	
	public void eat(){
		System.out.println("女人少吃,为了减肥。");
	}
	
	public void walk(){
		System.out.println("女人,窈窕的走路。");
	}
}

public class Man extends Person{
	
	boolean isSmoking;
	
	public void earnMoney(){
		System.out.println("男人负责工作养家");
	}
	
	public void eat() {
		System.out.println("男人多吃肉,长肌肉");
	}
	
	public void walk() {
		System.out.println("男人霸气的走路");
	}
}

对象的多态性:父类的引用指向子类的对象(或子类的对象赋值给父类的引用)

Person p1 = new Man();

Person p2 = new Woman();

可以理解为一个事物的多种形态

p1.eat();p1.walk();

输出:男人。。。

p2.eat();p2.walk();

输出:女人。。。

同样是Person类的引用,但是指向的对象,一个是男人/一个是女人。在男人和女人类中都改写了Person类的方法。所以表现出来的是不同的方法。

当调用子父类同名同参数方法时,实际调用的是子类重写父类的方法,这叫作虚拟方法调用

可能对多态的一个误解

但是也会出现一个问题如果: p1.earnMoney() 报错

虽然p1能调用Person里面有的方法,但是执行的时候却是执行子类中重写的方法。

也就是java编译看左边(编译期只能调用父类中声明的方法),执行看右边(实际执行的时候,是子类重写父类的方法)

所以,请牢记:

编译看左边,执行看右边

多态的前提

满足下列的关系,才能在这此基础上谈论多态。

① 类的继承关系

② 方法的重写

程序为什么要有多态性?

举例

/*
 * 多态性应用举例
 */

class Animal{
	public void eat(){
		System.out.println("动物,进食");
	}
	
	public void shout(){
		System.out.println("动物:叫");
	}
}


class Dog extends Animal{
	public void eat(){
		System.out.println("狗吃骨头");
	}
	
	public void shout() {
		System.out.println("汪!汪!汪!");
	}
}

class Cat extends Animal{
	public void eat(){
		System.out.println("猫吃鱼");
	}
	
	public void shout() {
		System.out.println("喵!喵!喵!");
	}
}

public class AnimalTest {
	
	public static void main(String[] args) {
		AnimalTest test = new AnimalTest();
		test.func(new Dog());
		
		test.func(new Cat());
	}

	public void func(Animal animal){	//Animal animal = new Dog();
		animal.eat();
		animal.shout();
	}
	
	//如果没有多态性,就会写很多如下的方法去调用分别子类的方法,造成代码的重复
	public void func(Dog dog){
		dog.eat();
		dog.shout();
	}
	
	public void func(Cat cat){
		cat.eat();
		cat.shout();
	}
}

多态性不适用于类的属性

public class Person{
    int id = 1001;
}

public class Man{
    int id = 1002;
}

Person p = new Man();
System.out.println(p.id)

输出1001

多态性不适用于属性,编译和运行都是看左边。

多态是怎么实现的?

虚拟方法的调用

之所以能够实现多态,是因为父类在生成方法的时候,是生成了一个虚拟方法 子类中定义了与父类同名同参数的方法。在多态情况下,将此时父类的方法称为虚拟方法.父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

//正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
//编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。也叫作动态绑定
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法

方法的重载与重写

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

instanceof 关键字

判断A是不是B的一个实例

A instanceof B

向下转型的几种情况

先看几种问题 一、编译通过,运行不通过。 情况1:

People p = new Woman();
Man m = (Man)p;

上面的代码,在编译的时候会通过,但是运行的时间会报ClassCastException错误。

情况2:

People p = new People();
Man m = (Man)p;

如果能通过,那m.earnMoney的方法在哪里?

二、编译通过,运行通过。

Object obj = new Woman();
Person p = (Person)obj;

三、编译不过

Man m = new Woman();