从零开始学Java【19】
从零开始学Java【19】
学习视频:https://www.bilibili.com/video/BV12J41137hu?p=68
注
本文中出现的所有“向上转型”和“向下转型”以及父类与子类的高位低位问题笔者尚未搞清楚,请忽略笔者的引导。笔者资质尚浅仍在学习中,请适当参考。
什么是多态

一个对象的实际类型是确定的,如:
new Student();
new Person();
但是,可以指向的引用类型就不确定了,如:
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
可以这么理解,等号的左边是引用类型,等号的右边是实际类型。比如下图,Student类继承Person类:

那么再回顾上面的三种:
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
s2就是父类的引用指向子类的类型,s3就是祖宗类指向子类的类型(以前讲过Object是所有类的祖宗),它们的实际对象都相同,都是Student(),而引用对象却可以不相同,这是符合规则的,new一个对象的时候可以根据这里所说的方法进行命名,这种方法叫做向下转型。
比如在Person类写个方法,用s2来执行:

可以看到,s2执行了父类的方法,这时候再在Student重写一个方法:

但是这里可以发现,因为子类重写了父类的方法,所以s1调取方法的时候,首先看它本身所处在的这个类,即Student类有没有这个方法,如果有则输出,如果没有,就返回父类找这个方法。
在这里可以看到向上转型的好处:多态里面说,同一个方法可以根据发送对象的不同而采用不同的行为方式。比如例子里面,s1是子类类型,调用子类的test方法,s2是父类类型,调用父类的test方法,实现了方法的多样化。
上面那种情况是父类写了个方法,子类再重写,那么下面如果子类写个方法而父类没有呢?比如子类写一个eat方法:

可以发现,s2是直接调用不了子类的eat方法的,会报错的,当然,可以通过一定方法调用,向下转型,这个后面讲。
那么为什么s2不能直接调用eat方法呢?
因为通过观察。s2的类型是Person类,是父类,前面说了,非静态的情况看等号左边,它这里等号左边是Person,意味着s2是Person类的一个对象,那么Person类没有eat方法,自然会报错。
从这里可以总结出来一个经验:像s1,s2,s3这些对象能执行哪些方法,主要看对象左边的类型,和右边关系不大。
Student类能调用的方法都是自己的或者继承父类的,而Person父类可以指向子类,但是不能调用子类独有的方法。
通过如此,我看到了,像s1,s2,s3这些对象,我第一眼要观察它所属的类型,如果它是子类,那么我可以使用子类的方法,又因为子类能调用父类的所有非私有的方法,所以此时能发挥的空间很大。
而如果它是父类,那么我可以调用父类的全部方法,同时我要观察子类有没有独有的方法,如果我需要调用子类的独有方法,那么我需要向下转型来进行调用。
好,那么此时就是说明向下转型的时机了。
首先看看基本格式:对象类型.方法
顾名思义,就是什么样的类型,对应调用什么样的方法,我一个父类是不能随便调用子类的方法的,那么如果需要,我需要向下转型。如下:

这就是向下转型,s2是父类,((Student)S2)就暂时变成了子类了,然后就可以用子类的身份来调用子类的方法了。
类型转换的话,课程后面会继续学到的。
小总结
- Student子类能调用的方法都是自己的或者是继承父类的
- Person父类可以指向子类,但是不能调用子类独有的方法(除非向下转型)
- 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大(当然,大前提是非静态方法下才可以)【这个和后面的总结会有呼应的】
对于多态的总结:
- 多态是方法的多态,属性没有多态
- 类之间有联系才可以进行类型转换,比如父类和子类是继承关系的,才可以类型转换,否则会类型转换异常,异常名称:ClassCastException。这个异常名字先积累一下,后面学异常会用到。
多态的存在条件:
- 要有继承关系
- 方法需要重写。
- 父类引用指向子类对象。比如
Father s1 = new Son();
那么什么方法不能够被重写呢?
- static修饰的方法不能被重写,static方法属于类,不属于实例,上面重写是实例方法的重写
- final修饰的方法不能被重写,因为final方法在常量池里面。
- private修饰的方法不能被重写。
现在再来看这张图,就会明朗很多:

instanceof和类型转换
instanceof
参考链接:https://www.csdn.net/tags/Ntjakg5sMzM5ODItYmxvZwO0O0OO0O0O.html
java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
instanceof关键字主要作用是通过返回一个布尔值来判断两个类之间是否存在着继承关系。
用法为:
result = object instanceof class
参数:
Result:布尔类型。
Object:必选项。任意对象表达式。
Class:必选项。任意已定义的对象类。
说明:
如果 object 是 class 的一个实例,则 instanceof 运算符返回 true。如果 object 不是指定类的一个实例,或者 object 是 null,则返回 false。
在实践之前,先搞懂Object object = new Student();这一句话。
注意
Object object = new Student()这个语句,涉及到多态,而多态,有下面这个说法:Java有两种引⽤类型,分别是编译时类型和运⾏时类型。编译型类型在变量声明时决定,运⾏时类型取决于变量具体指向的类型,如果两种类型不⼀致,就会出现多态。其中等号左边的Object是编译类型,等号右边是运行类型

即编译时看左边,运行时看右边,且运行类型是可以变化的。
这里看看这篇文章:https://blog.csdn.net/qq_48508278/article/details/118544295
以下节选自文章:
Person p=new Women() (Women类继承自Person类) 那么,假如p的属性修饰符为public 访问属性时得到的是Person类的属性还是Women类的属性,方法调用又是哪个类?答案:会得到Person类的属性,调用Women类的方法。
这里可以看到,对象p访问属性是看编译时的Person类的属性,访问方法是看运行时的Women类的方法。
实践:
先Person为父类,Student和Teacher继承Person父类,然后第一种情况
- 如代码与下图:
Object object = new Student();

首先,object编译时是Object类,运行时是Student类,访问属性时是访问Object属性,访问方法是访问Student方法。再看instanceof的定义,java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例,注意是在“运行时”作用的,所以此时object对象是认定为student类的。
其次,有着这么一层关系:
- Object > Person > Student
- Object > Person > Teacher
- Object > String
Student和Teacher是同级的,因为不在同一条线上,没有继承关系(即没有特定类的实例关系)。
object代表Student类,所以可以得到上图所说结果:
①Student类和Student类自己比,是true
②Student类和Person 类比,是继承关系,是true
③Student类和Object类比,是继承关系(祖宗类),是true
④Student类和Teacher类比,没有继承关系,输出false.
⑤Student类和String类比,没有继承关系,输出false
- 再换个实例来,如代码与下图:
Person person = new Student();


person编译时代表person类,运行时代表Student类,所以可以得到上图所说结果:
①Student类和Student类自己比,是true
②Student类和Person 类比,是继承关系,是true
③Student类和Object类比,是继承关系(祖宗类),是true
④Student类和Teacher类比,没有继承关系,输出false
⑤因为是在编译时,代表的是person类,而person类又和String没有任何关系,和前面的object类不一样,人家编译时好歹是Object,编译不报错是能运行的,而现在这个编译时是Person类,只能注释掉,不然运行不了,而运行时则是前四种情况了。
3.再换个实例来,如代码与下图:
Student student = new Student();


student编译时是Student类,运行时也是Student类,那问题简单了:
①Student类和Student类自己比,是true
②Student类和Person 类比,是继承关系,是true
③Student类和Object类比,是继承关系(祖宗类),是true
④编译和运行时都是Student类,而Student类与Teacher没有任意继承关系,那么就会报错
⑤编译和运行时都是Student类,而Student类与String没有任意继承关系,那么就会报错
//Object > String
//Object > Person > Teacher
//Object > Person > Student
总结
x instanceof Y ,其实就是看X和Y的关系有没有构成直接或者间接继承的关系,同时,分三步走:
- X编译时,代表的是等号左边的类。
- X运行时,代表的是等号右边的类。
根据两种不同的情况,可以在人工排查的时候手动查询报错的原因以及预测Boolean值。
类型转换
我们前面学的基本类型转换,int,float,double那些,低位转高位则不用做什么,如果高位转地位则需要进行强制转换,同时也会损失一些精度。本节的关于父类和子类之间的强制转换也有异曲同工之妙,如果强制转换,其实也是会损失一些方法的。
如下面代码:

从Person类转到Student类,从低位到高位,需要强制转换,转换格式为【((需要转的类型)对象名).方法】即可
参考文章:https://blog.csdn.net/qq_48508278/article/details/118544295
父类引用指向子类对象,我们称之为向上转型,属于自动类型转换
向上转型后父类引用变量只能调用它编译类型的方法不能调用运行时的类型方法,(即调用父类当中的方法)
如果想要调用运行时的类型和方法 则可以 强制向下转型(即调用子类的方法)

编译类型的看左边,所以,此时的obj是Person类型的。也可以对应前面总结的一句话:非静态的情况看等号左边,像s1,s2,s3这些对象能执行哪些方法,主要看对象左边的类型,和右边关系不大。
总结
如Student类继承Person类
Person x = new Student();
父类的引用指向子类对象。
如果x想要调用Student的方法,则需要向上转型。此时的x是看作为父类,那么通过低位转高位,则可以调用子类了。
所以下面可以有个结论:
- 父类转子类,是向下转型,相当于高位转低位,精度缺失,同理,方法也就可能有缺失了,所以不用这个会好一点。
- 子类转父类,是向上转型,相当于低位转高位,没有精度缺失,同理,也就没有方法缺失了。
有一篇优质文章,说向上/向下转型的:https://blog.csdn.net/gh2391292/article/details/74421308

浙公网安备 33010602011771号