Think In Java读书笔记:内部类覆盖及其初始化
本文相关章节:第十章 内部类 10.10 内部类可以被覆盖吗
在读至本节第二个范例代码时(及下方的代码),我对输出结果中的第一个“Egg.Yolk()”很不理解,为什么它会第一个地方输出。
我起初认为是在调用BigEgg.Yolk的构造器时初始化其父类调用父类构造器输出的,毕竟在main方法中第一行是在调用BigEgg的构造器,而它的构造器中首先调用了BigEgg.Yolk的构造器,在调用BigEgg.Yolk构造器时初始化其父类Egg.Yolk才输出的“Egg.Yolk()”。但书中却明确说明了第二个“EGG.Yolk()”是子类BigEgg.Yolk初始化其父类输出的,这就使我产生了迷惑——那第一个“Egg.Yolk()”到底是怎么输出的呢?
(书中范例代码,原文中因为是第二个范例所有的类都命名为“xxx2”,在这里我将所有的2都删去了)
/** * Created by dsa44 on 2018/4/21. */ class Egg{ class Yolk{ public Yolk(){ System.out.println("Egg.Yolk()"); } public void f(){ System.out.println("Egg.Yolk.f()"); } } private Yolk y = new Yolk(); public Egg(){ System.out.println("new Egg()"); } public void insertYolk(Yolk yy){ y = yy; } public void g(){ y.f(); } } public class BigEgg extends Egg{ public class Yolk extends Egg.Yolk{ public Yolk() { System.out.println("BigEgg.Yolk()"); } public void f(){ System.out.println("BigEgg.Yolk.f()"); } } public BigEgg(){ insertYolk(new Yolk()); } public static void main(String[] args){ Egg e = new BigEgg(); e.g(); } }/* 输出结果 Egg.Yolk() new Egg() Egg.Yolk() BigEgg.Yolk() BigEgg.Yolk.f() */
这其中的关键点也是书中没提到的我认为就是BigEgg构造器到底是在何时开始运行的(实际上也只有BigEgg的构造器没有输出任何信息),于是我在BigEgg的构造器中添加了输出“new BigEgg()”的语句。结果如下:
我想在这里就已经真相大白了:
在调用BigEgg构造器时,其构造器首先调用了其父类Egg的构造器(即首先初始化它自己的父类),在初始化父类Egg时首先初始化了父类的数据域,而它的父类Egg的数据域只有一个私有的Egg.Yolk类型的引用y,且在这里对y进行了初始化,调用它的构造器Egg.Yolk(),故输出了第一句“Egg.Yolk()”。
在初始化Egg的数据域后便运行了Egg的构造器(输出“new Egg()”),在自己的父类完成初始化后BigEgg的构造器开始运行(输出“new BigEgg()”),然后运行父类Egg的方法insertYolk,在其参数部分运行BigEgg.Yolk的构造器,在这里就变成了我刚开始提到的那一部分,于是相继输出了“Egg.Yolk()”“BigEgg.Yolk()”,最后虽然BigEgg.Yolk向上转型,但因为动态绑定的关系,在调用e.f()时,依旧输出的时属于BigEgg.Yolk的f方法,输出“BigEgg.Yolk.f()”。
总结:在这里遇到问题的主要原因还是因为对于第五章初始化部分了解的不够详细(知识点:父类初始化是在子类构造器未运行前就进行的,也可以说是在调用子类构造器时就开始了父类的初始化并在其初始化完成后开始运行子类的构造器)。