JAVA的两个问题

  2018-4-16 


今天在写工厂的时候,对于这样一个语句,遇到了两个问题

mom obj = new son();

以下为源代码

0FEEB447832EE00327DC103C3A4A6B57

输出的结果为

0

Type son

一、

这就很奇怪了。按照已知知识,mom obj = new son();这句话是在堆上新建一个son类型的对象,然后向上转型为mom类型。此时的obj,既是属于son类型,也属于其基类mom类型。这是java多态性的一个具体体现。对象实体是son类型的,那么我在调用son中的方法test的时候,和输出变量a的时候,理想输出应为

1

Type son

然而结果却不是。方法调用的是子类的方法,然而成员变量却调用的是基类的变量值。

我又运行了如下代码,更奇怪的事情发生了。

我在son类中加入了一个新方法test2()。在主函数中调用obj中的test2方法。

屏幕快照 2018-05-15 下午8.49.52

更奇怪的事情发生了。

29ECB79D17CD24DFC8F94CD0615777E2

为什么编译器不认这个儿子了?

经过多次测试,和查阅资料,我终于知道了原因。

首先要知道,java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法否是后期绑定的(动态绑定)。(final可以关闭动态绑定,但实际上,这对性能提升并不大,所以不要试图用final来提升性能,而是根据设计决定是否使用final)

向上转型,在运行时,会遗忘子类对象中与父类对象中不同的方法。也会覆盖与父类中相同的方法–重写。
所以obj可以调用的是mom中有的方法,而son中有,mom中却没有的方法,是不能调用的。
java 的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法, 但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

于是我们可以知道,向上转型机制,实际上遗失了子类的成员变量和基类没有的方法。

这就解释了为什么是这样的结果。

二、

在探究这个语句的执行过程的时候,就又引申出了另一个问题。构造器的继承问题。

对于mom obj = new son();

按照已知知识,mom obj是建立了一个mom类型的引用obj,相当于c中的一个指针。此时由于没有对象实体,其值为null(空指针)。但是当我在执行以下代码的时候,却发生了很奇怪的事情。

对于以下语句

屏幕快照 2018-05-15 下午8.38.42

输出结果为

mom

son

奇怪了,难道是mom obj这个语句的时候就已经进行了对象的创建和初始化?

我试了一下,如果只是

屏幕快照 2018-05-15 下午8.40.24

那么结果为

屏幕快照 2018-05-15 下午8.40.33

说明了mom obj语句并没有创建和初始化对象,只是声明了一个mom类型的引用。

于是我查阅书籍,在《thinking in java》看到了如下几段

“类的代码在初次使用的时候才加载。这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或static方法的时候,也会发生加载(构造器也是static方法,尽管static关键字并没有显示地写出来。因此更准确地讲,类是在其任何static成员被访问的时候加载的。”

所以说实际上,

在加载类的时候,会依次访问加载基类(或叫父类,超类),其中在遇到static对象和代码段的时候,会依程序中的顺序初始化。(而构造器也是static方法,只是没有写出来而已。

所以举例 对继承关系 A->B->C->D

在生成D类对象的时候,会执行构造器且构造器执行顺序为A->B->C->D。

这就解释了为什么是这个结果。


且听风吟