多态
1.方法体现多态
方法重载体现多态
对象通过传入不同数量的参数,会调用不同的sun方法,体现出多态
方法重写体现多态
A类和B类是继承关系,通过不同对象调用对应类中重写的方法体现
2.对象体现多态
编译是javac,运行是java
(1)一个对象的编译类型和运行类型可以不一致
将父类的一个引用指向子类的一个对象(等号右边是新建对象,左边只是引用,使animal指向对象),animal的编译类型是Animal,运行类型是Dog。言外之意是父类的引用可以接收一个子类的对象
此时animal编译类型和运行类型一致,都是Animal
(2)编译类型在定义对象时就确定了,不能改变
上述例子中animal的编译类型就是Animal,不能再改变
(3)运行类型可以改变
(4)编译类型看等号的左边,运行类型看等号的右边
运行到调用cry方法时,看此时对象animal的运行类型是什么,此时运行类型是Dog,那么调用的就是Dog类中的cry方法;当animal的运行类型是Cat时,那么调用的就是Cat类中的cry方法
理解!
方法形参列表中是Animail animal,是父类的引用,可以接收子类的对象
多态的细节(向上转型)
多态的情况下,子类重写了父类的方法,对象调用该方法时,访问的是子类的重写方法,对象还可以调用父类中所有与子类不同名的方法(这种方法没有被重写)
1.向上转型
Animal是Cat的父类Object也是Cat的父类,两个都是向上转型
3.
父类的引用指向子类的对象,该对象可以调用父类所有的成员,不能调用子类特有的成员(注意是特有的,要是进行了方法的重写仍然可以调用子类的方法)
注意:以下只针对对象调用方法的例子,访问属性的规则和访问方法的不一样
在编译阶段(javac),由编译器来指定你能调用哪些成员(通过编译类型来查找能调用的成员),此时能调用的是编译类型所在类的成员,由编译类型来决定;运行阶段,由java来决定,此时,看你的运行类型。因此,在编译通过后,进入运行阶段,先在运行类型所在的类(子类)中查找是否有该方法,有且拥有访问权限就调用,没有就往上查找
//上述案例中,编译通过后,找到animal对象的运行类型,是Cat,因此在animal.eat()时先从Cat类查找是否有该方法;若找不到的话再向上查找,而且不能调用子类特有的方法
//在Cat类中找到eat()方法,因此最终调用的是Cat中的eat方法
向下转型
1.语法:子类类型 引用名 = (子类类型) 父类引用名,()不能少
- & 3.
父类的引用必须指向当前目标类型的对象,即:animal对象在创建时,Animal animal =new Cat(),这时指向的就是Cat类型的对象(运行类型是Cat),而将animal进行强制类型转换,得到的目标类型也必须为Cat,不能是与Cat类同级(都是Animal的子类)的引用;此步骤执行完,对象引用cat指向Cat对象,与animal指向一样,cat的引用类型仍然是Cat,编译类型是Cat,因此该对象可以调用子类Cat的成员。此方法与创建一个新的对象不一样,没有在栈中开辟一个新的空间。
属性重写
属性的访问规则
对象访问属性时只与对象的编译类型有关,编译类型是什么,就访问该类型类中的属性,base对象访问count,得到的值是10
sub.count得到的值是20
instanceof比较操作符
判断对象的xx类型(对象的运行类型)是不是xx类型或是不是xx类型的子类,返回一个布尔值
多态的练习1
Object object =“Hello”
第一句向上转型完整表达是 Object obj = new String("Hello");
第二句将obj强制转换成String类型,根据上面第一句,可以知道obj的运行类型是String类型,因此将一个String的引用指向obj指向一个运行类型也为String类型的即为向下转型
多态的练习2
创建类Sub的对象s,s的编译类型为Sub,运行类型为Sub。s.count访问属性,看编译类型,编译类型为Sub,访问Sub中的属性,结果是20。Base b = s相当于向上转型,将b指向s指向的地址空间,即指向原来创建的Sub对象。b == s,两个对象进行比较,比较的是地址,地址相同返回true。b.count看b的编译类型,为Base,因此返回10。最后一句b调用方法,看b的运行类型,为Sub,从Sub类中找该方法,返回20。
动态绑定机制
1.对象调用方法时(不仅仅只有对象.方法名(),还包括方法中调用其它方法,调用其它方法时也会和对象的运行类型绑定,先去对象的运行类型中查找有没有该方法),该方法会和对象的运行类型绑定
2.调用属性时,没有动态绑定机制,调用到类中的某个方法时,先在该类中寻找有无该对象的声明,有的话直接调用该对象的属性,不用根据对象的运行类型来查找
在上面案例,如果注销掉子类B中的sum()方法,a.sum()的过程为:根据对象a的运行类型为B,现在B类中寻找该方法,没找到向上查找父类A中的方法,找到父类中的sum()方法,方法体中为return getI()+10,此时又调用了一次getI()方法,根据动态运行机制,根据对象a的运行类型为B,又到B类中查找getI(),方法体中的内容是返回属性I,在B类中有属性I的声明,因此返回B类中的i,返回20,再回到原来的sum()中执行20+10,得到结果为30。
再注销掉sum1(),a.sum1()的过程为:根据对象a的运行类型为B,先去B类查找,找不到向上查找父类,找到父类A中的sum1(),执行i+10,在父类A中先查找有无局部变量i,没有,就找到父类A中的属性i(在A类中声明就返回A类的i),执行10+10,得到结果20
结果如下
标签:调用,Java,对象,子类,多态,第八章,Cat,类型,方法 From: https://blog.csdn.net/m0_68811842/article/details/142685070