Java类中,静态初始化器,构造函数,成员变量的加载顺序,一直没有仔细研究过。细节较不准确,所以编程吃了暗亏,纠结了几个小时才发现隐藏的错误。
于是自己做了一个小测验,算是祝自己理解Java类成员变量加载顺序这个问题吧。
测试代码如下:
package com.javaeye.aspnetdb;
/**
*
* @author aspnetdb 周洋
* 父类
*/
public class OrderFather {
public static String strMsg = "父类Msg";
static {
System.out.println("父类静态初始化器");
System.out.println("父类成员变量=" + strMsg);
}
public OrderFather() {
System.out.println("父类的构造函数");
System.out.println("父类构造函数中,父类成员变量=" + strMsg);
}
public void say() {
System.out.println("父类说点什么,子类还是父类=" + strMsg);
}
}
package com.javaeye.aspnetdb;
/**
*
* @author aspnetdb 周洋
* 子类
*/
public class OrderSon extends OrderFather{
public static String strMsg = "子类Msg";
public OrderSon(){
System.out.println("子类构造函数");
System.out.println("构造函数中,子类类成员变量=" + strMsg);
}
static {
System.out.println("子类静态初始化器");
System.out.println("子类成员变量=" + strMsg);
}
public void say() {
System.out.println("子类说点什么,子类还是父类=" + strMsg);
}
}
package com.javaeye.aspnetdb;
/**
*
* @author aspnetdb 周洋
* 测试的主类
*/
public class ClassOrderMain {
public static void main(String[] args) {
OrderSon son = new OrderSon();
son.say();
System.out.println("分咯线*****************************我咯咯咯");
OrderFather father = new OrderFather();
father.say();
System.out.println("分格线*****************************我格格格");
OrderFather father_son = new OrderSon();
father_son.say();
}
}
测试结果:
父类静态初始化器
父类成员变量=父类Msg
子类静态初始化器
子类成员变量=子类Msg
父类的构造函数
父类构造函数中,父类成员变量=父类Msg
子类构造函数
构造函数中,子类类成员变量=子类Msg
子类说点什么,子类还是父类=子类Msg
分咯线*****************************我咯咯咯
父类的构造函数
父类构造函数中,父类成员变量=父类Msg
父类说点什么,子类还是父类=父类Msg
分格线*****************************我格格格
父类的构造函数
父类构造函数中,父类成员变量=父类Msg
子类构造函数
构造函数中,子类类成员变量=子类Msg
子类说点什么,子类还是父类=子类Msg
可以看到,无论是父类还是子类,静态初始化器只执行一次,且父类的初始化器优先于子类的初始化器执行。
如果大家够仔细,debug一下,就可以看到父类还是子类,都是按照如下顺序来执行的。
初始化成员变量->执行初始化器->执行构造函数
第三个例子中,我顺便也测试了一下函数覆盖的问题。
可以看到,申请了子类大小的空间,调用从父类那里继承的方法,最后还是会访问实际申请出来的那个对象的方法。
方法中访问的成员变量也是实际申请出来的对象的变量。
注意:这篇博客只是做一个简单的测试,至于为什么是这个结果,我记得《Thinking in Java》中有写到过。以后如果有空,会不上原因分析的。