Java-面向对象特征-多态

多态polymorphic

基本介绍

初识例子

🔥多态的细节

例子

Java的动态绑定机制 Dynamic binding

多态的应用(多态数组和多态参数)


【基本介绍】

方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

具体体现:

  1. 方法的多态:重写和重载就体现多态

  2. 对象的多态(核心,困难,重点)
    重要的几句话:
    (1)一个对象的编译类型和运行类型可以不一致
    (2) 编译类型在定义对象时,就确定了,不能改变
    (3) 运行类型是可以变化的
    (4) 编译类型看定义时 =号的左边,运行类型看 =号的右边
    运行类型可以用 getClass()方法查看;

案例:Animal animal = new Dog();
(Dog类继承于Animal类)
(animal编译类型是Animal,运行类型Dog)


【初识例子】

image
说明:Dog和Cat是Animal的子类,Bone和Fish是Food的子类


【多态的细节】

细节一:

多态的前提是:两个对象(类)存在继承关系。

多态的向上转型

父类的引用指向了子类的对象
Person p = new Student();😭

(1)语法:父类类型 引用名 = new 子类类型();
(2)特点:编译类型看左边,运行类型看右边。

可以调用父类中的所有成员(需遵守访问权限);

不能调用子类中特有成员;【在编译阶段,能调用哪些成员,由编译类型来决定的】【向下转型后可以】

最终运行效果看子类的具体实现。

image

若有对象:Aniaml animal = new Cat();

animal.catchMouse(); //编译报错

【编译类型】则该对象的编译类型是Animal,可以调用父类的sleep等方法,但是不能调用Cat类的catchMouse方法。不能调用的原因是编译器不通过,因为其编译类型是Animal,Animal类没有该方法

animal.eat();

animal.run();

animal.show();

animal.sleep(); //都可以运行

【运行类型】该对象的运行类型是Cat,所以当运行时,它还是先从Cat类中寻找这四个方法,找不到再往上级父类找,然后调用(当子类重写父类的方法时,此时可以调用子类的方法)


细节二:

多态的向下转型

把指向子类对象的父类引用,转成指向子类对象的子类引用
Student p1 = (Student) p;

(1)语法:子类类型 引用名 = (子类类型)父类引用

Cat cat = (Cat)animal;

cat.catchMouse(); //可以运行

(2)只能强转父类的引用,不能强转父类的对象

(3)要求父类的引用必须指向的是当前目标类型的对象

Dog dog = (Dog)animal; //出现异常,如下图,它本该是只猫

(4)当向下转型后,可以调用子类类型中所有的成员

image

相当于说,现在有两个引用指向这个cat对象,一个是父类的引用,一个是子类的引用


细节三:

属性没有重写之说,属性的值看编译类型

image

instanceof 比较修饰符,用于判断对象的运行类型是否为XX类型或XX类型的子类型,运算的结果是true或false


【练习】

image

image

image


【Java的动态绑定机制(非常非常重要)Dynamic binding】

  1. 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

  2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用

image

由代码可得,对象a的编译类型是 A ,运行类型是 B
所以a.sum()和a.sum1()都从运行类型 B 开始找。

若仅仅把子类的sum()方法注释掉,则此时的输出为 30 ,30
(运行时找到父类的sum方法,但是里面又包含一个getI方法,这个方法父类和子类都设置有
根据动态绑定机制,这个getI方法会调用运行类型的该方法。)(若子类没有这个方法,往上级父类找)

若仅仅把子类的sum1()方法注释掉,则此时的输出为 40 ,20
(运行时找到父类的sum1方法,但是里面有一个属性i,这个属性父类的子类都设置有;
根据动态绑定机制,这个i属性会调用此方法所在的类的属性。)


【多态数组】

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

image


【多态参数】

方法定义的形参类型为父类类型,实参类型允许为子类类型

image

public void showEmpAnnual(Employee e) {
	System.out.println("该员工"+e.getName()+"的年工资为:"+e.getAnnual());
}

public void testWork(Employee e) {
	if(e instanceof Worker) {
		Worker worker = (Worker)e; //向下转型,调用子类的方法
		worker.work();
	}else if(e instanceof Manager) {
		((Manager)e).manage(); //向下转型,调用子类的方法
	}
	
}
posted @ 2021-09-02 22:41  Wiiiimp  阅读(80)  评论(0)    收藏  举报