关于Java种成员变量加载的一个实验
关于Java类成员变量加载的一个实验
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》中有写到过。以后如果有空,会不上原因分析的。