00:00
好,那昨天呢,我们关于这个多态性的核心内容,实际上呢,昨天也都已经讲完了,那今天的话呢,我们在讲呢,是关于这个多态性的一些补充性的内容,当然呢,补充性内容也很重要啊,也很重要,那我们这来看一下,剩下这个内容呢,这里提到一个叫instance of关键字的一个使用,这呢是一个关键字,咱们在讲这个运算符的时候呢,提到过这个instance of提到过它,那我们说呢,当时讲说放到多肽的时候再讲,就是放到这来讲,那instance of这个关键词怎么使用,为什么要用这块呢?我们还是直接从代码这个层面呢,给大家来引入,那这个代码的话呢,咱们还承接着昨天写的这个多态性来举例子,昨天呢,多态性咱们是通过这个person慢woman,还有这个test呢去体会的,我这时候呢,CTRLC一下就不再重新去写这个类了,咱们再新造一个工程,对13。在这个src下呢,新创建一个包扎这包,然后CTRLV一下,把刚才呢,我们这样的几个类呢就拿过来了,稍微的再温析一下,这个类person里边定义过属性,定义过两个方法,然后在它的子类man和woman当中,除了我们继承父类以外,拿到父类中的结构之外呢,它还定义了自己的属性和特有的方法,对父类中的方法呢,还进行了重写,这个woman呢也同样的道理,然后呢,咱们在这个person test当中去体会这个多态性的一个使用,核心的内容就是昨天我们讲的这样的五个点,那在这个之外,咱们今天呢,来说剩下的一个内容这儿呢,我打一条线在这儿接着去说。
01:39
那剩下的内容我们这样呢来引入,通过这呢,我也打一条线。下边呢,我们来接着去讲,大家看我们上面的话呢,说用了这个多态性了,就是我们这里的这个P2,这个P2的话呢,我们声明的是一个person,但我们实际new的时候呢,是一个子类对象,那而且我们在调用方法的时候呢,也体现了叫虚拟方法调用,说呢,我们编译时候呢,认为是父类中的方法,实际执行呢,是子类中所特有的方法,呃,子类中重写的方法,这呢就是我们多态性的体现了,这是核心的内容,那现在呢,我们在这个之外呢,再来看一个事儿,说呢,我们这个new的P2。
02:24
声明的P2实际用的呢是个慢,咱们在编译期认为这个P2呢,就是一个person,所以说呢,我们只能够调用person当中声明的属性和方法,属性的前提是你你能调性,就如果权限够你能调可以,那也就是说呢,我们在这个,嗯,我把我把这个操作呢再写到这吧,CTRLCL一下我们在这个。嗯,代码当中是不可以在这种多态性存在的条件下呢,通过这个父类的引用呢,去调用子类所特有的方法的,不能调用子类所特有的方法,原因呢很简单,就是我们编译时这个P2呢,它就是一个person类型,那么在这个person里边呢,我们就没有定义过这个个money这样的方法,包括属性呢,也是一样属性,咱们目前那个person,你看我这个权限都没有,没有没有设置,其实呢,是一个默认权限了,我们A,呃,Name age这个这个的ID,我们在咱们的这个percent test当中,我现在呢,通过这个P2呢,你去调一下name呢,是OK的啊,没有问题,咱们那个子类,咱们用的是个man,子类中呢,我定义过叫is smoking,那is smoking的话呢,权限我也没有加,那其实的话呢,按权限上来。
03:52
啊是可以调的,但是事实上呢,你发现我们通过这P2呢去调这个调is smoking,对你也调不了,比如我想给他付个处说呃,抽烟这个掉不了,这个呢,你想这个这个比如说附一个值叫Tom,这个是OK的。
04:13
上面也行,因为呢,你认为它就是一个person了,文章就可以去调person当中的有的结构,而我们这个in smoking呢,这个调不了,它这也提示说你这个没有定义过,这个不是你单词写错了,你要怕单词写错了,你把它那CTRLCL一下,就别自己在这去写了,CTRLV一下,这样粘肯定粘不错了,那这种情况下呢,它仍然说你没有定义,它可不是说不可见啊,这时候要从可见性上来讲,实际上是可见的,就是因为我这个权限你在这个同一个包内,这不是说都可以调嘛,从这个封装性来讲,是按说是可见的,但是呢,我调不了,他提的提的意思说你这个没定义过,就是因为咱们编译的时候呢,认为这个P2呢,就是P类型的了,所以说呢,这个方法也好,属性也好,我们都不能调用子类所特有的啊,方法加上一个属性。
05:04
不能掉,这是第一个现实,那第二个问题呢,我们就要说了。问大家一个问题,你说呢?此时我们内存当中是否加载过像earn money,像is smoking这样的属性或方法?或者问的再直接一点,你说我们new了一个这个结构啊,我在这是占空间,这是对空间,我在这个堆里边new了一个慢,这个慢里边有这个像name啊,Age啊,ID啊,这是我们这个person中构造其中声明的,然后我们这个慢中呢,也声明过一个ID,还有一个叫is smoking,你说内存中有没有指达属性没有,有同学非常坚定说没有,没有的话呢,你前面讲的那知识不是白讲了吗?前面的知识就有错误了,说我们造一个类的对象,然后你这个类当中声明的那个属性在队里边就有,那这句话就错了。那不坏坏事了吗?是吧,嗯,这个呢,肯定是有的。
06:02
哎,这个我们前面讲过那个知识呢,得一脉相承下来,这是对的,就是我们呢,New一个类的对象,目前呢,这个属性咱们都是非static的啊,像这些属性的话呢,都会加载到咱们堆空间中你这个对象里面,言外之意呢,就是我们在慢当中定义的ID和is smoking的属性,在你整个new的这个慢对象里边是一定有的,但是由于我们把这个变量呢,声明成是person类型的了,我们呢,只能够调这些person中剩下的结构了,这两个结构呢,相当于给我们屏蔽掉了,掉不了了,方法也一样,既然呢,我们new过这个慢,你除了加载负类的那些属性方法之外呢,你自己肯定也是要加载自己的,那你加载了自己,那就意味着你的属性之外呢,方法也是会加载过来的,只是呢,由于你声命的类型是person类型的,我现在呢调不了了,是这个原因造成的,所以我们下一个问题就是说我怎么才可以调用。
07:03
这个我内存中有的这个属性,或者说这个慢当中定义的特有的方法。这是我们要解决这个事儿,我们把这个事呢,先说一下,嗯,这个这样来说,嗯,这个这个多肽子类对象的多态性以后,有了对象的多态性以后,就是咱们说的这样的一个形式,有了它以后呢,我们说这个内存中实际实际上是这个加载了,加载了这个子类这个特有的。属性和方法的,但是呢,由于声明为负类类型,或者由于这个变量声明为负类类型,嗯,负类类型导致呢。
08:06
这个编译时只能识别父类中所声明的属性和方法。导致编译时只能够只能调用父类中声明的这个属性和方法,这个子类呢,特有的这个属性和方法呢不能调用,这呢就是我们目前这样的一个现实,这里边呢,明确两个点,第一个呢,就是说它确实有这个在内存中呢,确实也加载了,那第二个问题呢,就是说我们调不了。啊,原因呢,就是因为你生命呢是一个负类类型编译的时候呢,咱们是看左边了啊这个事儿呢就不好使了,那接下来我们要考虑的就是那如何才可以去调用子类中所特有的属性和方法。
09:02
如何才能调用子类特有的属性和方法?你要想能够调用子类所特有的属性和方法,那你相当于对于我们编译器来讲,它必须要求我看到的这个P2不再是一个person类型的才可以。对吧,你不是P类型了,我我编译就是看左边了,你现在要想能调子类你所特有的,那只能是把我那个person类型给它改了,那就相当于我们现在必须呢,是不是把它得生命成是个慢类型的,那我这呢,我就写一个M1,那P2你要这样写的话呢,显然是不允许的,对,因为咱们Java当中这个叫赋值符号,咱们也说过好多次了,赋值符号呢,要么就是你右边跟左边类型一样,要么呢,就是咱们讲基本数据类型,有个所谓的自动类型提升的事儿是可以的,那你现在是个类类型的不行。我这是一个子类的对象,子类对象可以往父类这块去负,就是子的可以负给父的,你现在是不是反过来了,父的负给子的,这就不行了,那怎么用呢?对咱们这这块呢,诶跟咱们前面讲的那个自动类型提升的一个逆运算呢,有点像了,叫强制类型转换,我把生命为person类型的这个变量呢,强转成是一个慢类型,强转化以后,那它就可以这样写了,编译器呢就识别了。
10:27
哎,这就是OK的,那那这呢就说如何才能调用子类所特有的属性和方法呢,我们相当也叫,哎使用强制类型转换符,嗯使用强制类型转换符,那么把这个事的话呢,咱们也可以称作呢,叫向下转型,那线下转型,那这呢,有了这样一个概念,咱们先等等,再往下接着写,咱们先回过来呢,把这个线下转型这个事呢,给大家去强调一下。去强调一下,这块呢就有了一个类比了,我们学这个事儿的话呢,就更容易一些,前面的话呢,咱们讲基本数据类型的时候提过叫自动类型提升和强制类型转换,就是说我这有一个int型的一个变量,我想给它转换成个double类型的,这呢你直接把这个值呢复过去就行了,这个我们叫做自动类型提升,把它呢可以称作叫容量小的或者叫低级的数据类型,然后double呢,可以称作叫高级的或者叫容量大的数据类型,那低级的这个呢,直接可以转成高级的,那我现在有一个double类型的变量,我想呢,给它转换成一个int,那我们需要使用这个强制类型转换,用的就是一对小括号,括号里边呢,指明你到底想转换的这个类型是什么,这个大家已经很熟了,前面讲过了,那现在呢,其实迁移过来呢,咱们在多态性这块呢,有跟它类似这样的一个知识。
11:52
我们呢,这有一个子类对象,这个子类对象呢,我想给他付给一个父类,你可以把在这称作就像这个低级的数据类型一样,它呢就相当于是高级的一样,我们有一个student的一个对象,我直接呢付给负类是OK的,有点像自动型提升,但是我们这块呢,不叫自型提升了,也平常也不提叫向上转型了,我们都提叫多肽了,实际上这个多肽呢,就相当于是向上向上转型,你不是转成它的负类的类型了吗?
12:20
就这个意思,那么对应过来,我现在呢,声明的是一个person类型的一个变量了,我怎么能够给它转成student呢,相当于就是我们往下去转,往下去转,我们这时候呢,也是需要使用叫强制类型转换符,跟这个呢一样,所以这个知识的话呢,其实这样一迁移,大家就很容易理解了,OK,那我们再回过来,那你要这样来说的话呢,我们有了一个向上转型的概念,那其实这个向上转型呢,指的就是多态,它就相当于是一个向上转型啊,大家体会一下,但是一般呢,我们很少说向上转型啊,就直接说多肽了,那我现在呢,就使用这个强转符转成了一个慢转成以后呢,我再拿着这个M1,我再去调所谓的叫earn money,再去调这个M1,比如我去调,这叫is smoking,大家看呢,就都可以掉了。
13:13
这能调呢,也只是说明我们编译层面的能调,那真正呢,运行时候会不会出问题,我们得跑一下才知道运行OK。大家发现呢没事,这个呢,这不就我们这个earn money嘛,下边这呢,我付了个值也没输出,所以说你就看不到这个效果了,实际上呢,都没有问题,那么这时候在内存中怎么去看待呢?实际上是这样的。我们呢,一开始在这生命了一个叫P2,我们用了一个慢,用了一个男人,这里边呢,其实是有person中生命的属性,和我们这个慢中生命的这个属性都有的,我这有个地址值付给了我们这个P二指过来,这个地址值咱们当时讲的时候,其实说过这个地址值的话呢,准确的来说,它其实包含两部分。
14:00
前面一部分呢,是一个类型,然后艾特的时候是不是写了一个具体的地址啊对,然后呢,因为这个有类型的限制,所以呢,当我们试图呢,给他就付给这个M1的时候,他告诉我说不行,就是直接咱们想不加这个强制应转换符的时候呢,不让付,因为它前面有个类型,所以不让你付了,但是我们加上这个慢以后呢,就表示我现在想强转了,强转的时候呢,它就可以了,就是这时候类型呢,就还是你自己的这个类型的,然后呢,我这时候艾特呢,这个地址完全不动的给它复制过来。复制过来以后呢,导致我们的M1呢,又也指向它了,但是由于我们此时这个M1我识别为是一个慢了,编译器呢认为它是个慢,所以我们这个时候呢,再去调它里边的自己特有的这个结构呢,就可以掉了啊,就可以掉了啊,虽然这个P2呢不能调,但是你要知道P2声明以后呢,照样还是有的,那有没有,那全看人家这个慢了。诶诶,这个上面给挡住了,全看你上面这个慢了,我你我慢那肯定就有。
15:04
就这样子一个关系,行这呢,就咱们说的这样的一个情况啊,哎,我们呢,使用强转符呢,就可以去调用了,但是这种写法的话呢,我们说有风险,凡是这种写强转的这种操作呢,其实都会有一些风险,咱们讲基本数据类型的时候,它的风险就在于精度会有损失。精度可能会有损失了,那在咱们这块呢,有可能啊,就是转不成功,你看我举个例子,咱们这个P2,我现在转成一个男人,我这个二呢,现在想转成个女人,转成这个女人,你看诶编辑器过了啊,然后通过这个we,我去调这个女人所特有的方法叫go shopping。你说这女人能去go shopping吗?执行挂了,说class cast exception叫类型转换异常,说呢,我们这个慢呢,你不能够转换成是一个woman,能理解不就相当于是你看我们这本身是个男的,然后你把他呢,误以为他是个女的。
16:17
就像生活中一样,他其实是个男的,你以为他是一个女的,你就感觉没啥问题,就该牵手牵手是吧,然后呢,运行的时候呢,挂了,嗯,不是一个女的是吧,出现叫类型转换异常,这个我说的是这种run as是吧?嗯,这就出问题了,那所以说呢,我们在使用这个墙砖的时候呢,要小心一点,它是有可能会出现异常的,把这个事儿呢写一下使用强转时,使用强转时说呢,叫可能出现叫class cost。
17:00
一次exception的异常,这个呢,异常是我们首次见,就是说当你这个类型不能够转的时候呢,就会报这个异常,那为了我们写程序呢,当你一旦去写这个这个这个类型转换的时候啊,强制类型转换或者叫强压转型的时候呢,为了避免出现这样的问题,我们这块呢,引入了一个关键字,这个关键字呢,就叫做instance of。哎,就是他他这个关键字的一个使用啊,行先把这个呢,咱们先停一下。
我来说两句