《Java编程思想》学习02:继承类的初始化加载顺序分析
本文根据源码实例,分析继承类初始化的加载顺序;
参考《Java编程思想》第十章
思路:创建三个类动物Animal类、狗狗Dog类、哈士奇Huskie类,每个类中有一个非静态变量和无参构造函数,且Huskie类继承Dog类,Dog类继承Animal类;然后实例化一个Huskie类对象,根据输出的日志确认加载顺序
源码
Animal类
package com.tyj.study.thinkinjava.chapter8; /* * *@Description: *@Author:TYJ *@Date: create in 2019/7/10 8:59 */ public class Animal { private Double weight = getWeight(); private Double getWeight() { System.out.println("i am Animal getWeight method"); return new Double(1.0); } public void eat(){ System.out.println("i am Animal eat method"); } public Animal() { System.out.println("i am animal constructor"); eat(); } public static void main(String[] args) { Animal animal = new Animal(); /** 日志输出: i am Animal getWeight method i am animal constructor i am Animal eat method */ } }
Dog类
package com.tyj.study.thinkinjava.chapter8; /* * *@Description: *@Author:TYJ *@Date: create in 2019/7/10 9:00 */ public class Dog extends Animal{ private int legNum = getLegNum(); private String food = new String("meat"); private int getLegNum(){ System.out.println("i am Dog getLegNum method"); return 4; } public void eat(){ System.out.println("i am Dog eat method. dog eat " + food); } public Dog() { System.out.println("i am Dog constructor"); } public static void main(String[] args) { Dog dog = new Dog(); /** 日志输出: i am Animal getWeight method i am animal constructor i am Dog eat method. dog eat null i am Dog getLegNum method i am Dog constructor */ } }
Huskie类
package com.tyj.study.thinkinjava.chapter8; /* * *@Description: *@Author:TYJ *@Date: create in 2019/7/10 9:00 */ public class Huskie extends Dog{ private Boolean isStupid = judgeIQ(); private Boolean isLovely = judgeLovely(); private Boolean judgeIQ(){ System.out.println("i am Huskie judgeIQ method"); return true; } private Boolean judgeLovely(){ System.out.println("i am Huskie judgeLovely method"); return true; } public Huskie() { System.out.println("i am Huskie constructor"); } public static void main(String[] args) { Huskie huskie= new Huskie(); /** 日志输出: i am Animal getWeight method i am animal constructor i am Dog eat method. dog eat null i am Dog getLegNum method i am Dog constructor i am Huskie judgeIQ method i am Huskie judgeLovely method i am Huskie constructor */ } }
分析:
1-分析Animal类的main方法,可以确认,实例化一个普通类,会先初始化变量,再调用构造函数;(更具体的分析,请参考本人上一篇博客:《Java编程思想》学习01:普通类的初始化加载顺序分析》)
2-分析Huskie类的main方法,实例化一个子类,会递归找到最最上层的父类,然后按照继承的顺序初始化,本案例中,会依次初始化Animal类,Dog类,Huskie类;并且在舒适化每一个类的时候,先初始化变量,在调用构造器;
3-分析Dog类的main方法,输出了"i am Dog eat method. dog eat null",原因是因为Dog类实例化是,调用了父类Animal类的构造器方法,执行了eat();但因为Dog类重写了eat()方法,所以Animal类的构造器调用的是Dog类的eat()方法,但此时Dog类还没有被初始化,所以此时Dog类的food变量的值是null;所以输出"i am Dog eat method. dog eat null";
总结:
1-实例化一个普通类,会先初始化变量,再调用构造函数;
2-实例化一个有继承关系的子类,会递归找到最上层的父类,然后按照继承的顺序依次初始化每一个类;
3-实例化时,如果父类的构造器调用了被子类重写的方法,而子类方法有引用了子类的成员变量,则可能会出现因为成员变量未被初始化就被使用的情况;
4-目的:在实例化一个子类,必须先实例其对应的父类,以确保子类可以正常调用基类对子类公开的变量和方法。另外,《Java编程思想》给了一个编写构造器的建议:用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其它方法。