你知道子类引用为什么不能指向父类对象吗?反汇编一起看看

createh54个月前 (02-01)技术教程45

在java、C++等面向对象的语言中,实现多态的方式就是使用父类引用指向子类对象,所以父类引用指向子类对象是没有任何问题的,但是,大家有没有想过,子类引用可以指向父类对象吗?答案是不可以!但是为什么呢?


下图是在java中,使用子类引用指向父类对象的情况

编译可以通过,因为对生成的Person对象做了一个强制转换,骗过了编译器,其本质上还是属于子类引用指向父类对象。

点击运行,出现下图的报错情况。

很明显,java虚拟机在运行该行代码的时候进行了运行时检测,禁止子类引用指向父类对象。

所以,这种操作在java里面是不允许的,接下来,我们把代码拷贝一下,在C++的环境再跑一下。

编译,运行,一切非常顺利。

为什么java里面不允许这种操作,而C++却允许这种操作呢?我们接下来在C++的环境下,反汇编窥探一下这写代码究竟干了些什么事。


首先,在执行这行代码的时候,先把一个4压入栈中,然后去调用operator new这个函数,很明显,这个4就是该函数的一个参数,它完成的任务就是,向堆空间申请4个字节的存储空间,为什么是4个字节?因为Person这个类里面只有age这一个属性,因此new出来的对象也只需要4个字节存储就够了。然后使用stu这个Student类型的指针指向这4个存储空间的首地址。


接下来,我们来看一下,下面两行的反汇编代码,因为Student类继承于Person类,因此Student类里面有age和stuId两个变量,又因为是公有的,所以stu可以访问这两个变量的地址,我们对这两个值进行赋值操作。

这两个赋值操作的反汇编代码如下,可以明显看出,它们都是先找到stu指向的Person对象的堆空间首地址,然后当给age赋值为18时,是把12h(18的十六进制)塞给Person对象首地址位置开始的4个字节,当给stuId赋值为2时,是把2(2的十六进制)塞给Person对象首地址+4位置处开始的4个字节,


大家可以看到上图,很明显,相信大家就看出问题来了。。

因为new Person()只申请了4个字节的存储空间,而你现在却越界使用了没申请到的后面4个字节存储空间,然后把2赋值给了这4个字节的存储空间中。


这会导致什么问题?因为后面的这4个字节没有被你申请到,那么该4个字节可能是其他的一些数据,那么你的这个行为会覆盖掉别的数据,或者这4个字节还是空闲的,以后可能被其他的数据覆盖,所以这是一种不安全的行为。


因此,无论在C++还是java中,都是应该禁止掉这种行为的,只是java做了运行时检测,而C++并没有而已。

相关文章

JAVA教程全集-电子版(中)(java教程电子书下载)

第4章 面向对象的程序设计基础如前所述,Java语言是一种纯面向对象的编程语言,面向对象的程序设计是以类为基础的。从本章开始,我们将从类入手,详细介绍面向对象程序设计的基本思想和方法。本章将简要介绍面...

父类实现了Serializable,子类不需要实现Serializable

相关注意事项 a)序列化时,只对对象的状态进行保存,而不管对象的方法; b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口; c)当一个对象的实例变量引用其他对象...

产品设计阶段:To B软件产品设计流程总结

到了产品设计阶段,大部分产品经理(尤其是技术转型的产品经理)终于可以大大的喘一口气了,这个阶段的工作应该是产品人最最熟悉的环节了。网上关于产品设计(我总觉得这个叫需求分析)的方法论还真是多的很,场景分...

Java 最细的集合类总结(java中的集合类)

数据结构作为每一个开发者不可回避的问题,而 Java 对于不同的数据结构提供了非常成熟的实现,这一个又一个实现既是面试中的难点,也是工作中必不可少的工具,在此,笔者经历漫长的剖析,将其抽丝剥茧的呈现出...

JAVA学习基础之JAVA类集(java类中的类)

第十三章、JAVA类集在JAVA中必须掌握的知识点,会开发的:1、面向对象; 2、JAVA的类集; 3、JAVAIO; 4、JDBC;13-1、认识类集、Collection接口类集实际上就是一个动态...