链接:https://www.zhihu.com/question/51920553/answer/128610039
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我来说句,反对桑弘毅 的第一句:”在创建子类对象时,首先会调用父类的构造器,创造一个父类的对象“。
调用父类构造方法是真的,但是根本没有创建父类对象,只不过是调用父类构造方法来初始化属性。
如果说调用父类构造方法就等于创建父类对象,那就真的无稽之谈。
new指令开辟空间,用于存放对象的各个属/性引用等,反编译字节码你会发现只有一个new指令,所以开辟的是一块空间,一块空间就放一个对象。
然后,子类调用父类的属性,方法啥的,那并不是一个实例化的对象。
在字节码中子类会有个u2类型的父类索引,属于CONSTANT_Class_info类型,通过CONSTANT_Class_info的描述可以找到CONSTANT_Utf8_info,然后可以找到指定的父类啊啥的。
你的方法啊,属性名称都是在这个上面解析出来的,然后实际变量内容存储在new出来的空间那里。。。
super这个关键字只不过是访问了这个空间特定部分的数据(也就是专门存储父类数据的内存部分)。。。。。。
默认的hashcode和equals(直接使用的==比较)都是一样的,所以,这根本就在一个空间里,也不存在单独的出来的父类对象。
![](https://i-blog.csdnimg.cn/blog_migrate/a6cb8461ce03899fc83bda1d2a369c42.png)
如果说子类可以强行转换成父类进行使用,那是因为java虚拟机有个静态类型(外观类型)和实际类型的概念。
如Object t=new Point(2,3);
那么Object属于静态类型(外观类型),Point属于实际类型。
静态类型和实际类型在程序中都可以发生变法,区别是静态类型的变化仅仅发生在使用时发生,而变量本身的静态类型不会改变,并且最终的静态类型是在编译期间可知的;而实际变量类型的变化结果只有在运行期间才能被确定,编译器在编译的时候并不知道变量的实际类型是什么。
(这个是在分配方法的时候,但是我想在转换类型调用的时候也是这个道理。