标签:
题目:
public class InitTest{ public static int k = 0; public static InitTest t1 = new InitTest("t1"); public static InitTest t2 = new InitTest("t2"); public static int i = print("i"); public static int n = 99; public int j = print("j"); { print("Creat"); } static { print("Static"); } public InitTest(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++n; ++ i; } public static int print(String str){ System.out.println((++k) +":" + str + " i=" + i + " n=" + n); ++n; return ++ i; } public static void main(String[] args){ InitTest t = new InitTest("init"); } }
分析:
类需要在被实例化之前初始化,对象的初始化则是运行构造方法中的代码。
代码段分析:
2-6 行的是类的静态成员变量,需要在类加载的过程中被执行初始化;
第8行的int j则为实例成员变量,只再类被实例化的过程中初始化;
9-11 行为实例化的代码段,在类被实例化的过程中执行;
13-15 行为静态的代码段,在类被加载、初始化的过程中执行。
print(String str)为静态方法,其实现中牵涉到 k,i,n 三个静态成员变量,实际上,这个方法是专门用来标记执行顺序的方法;
InitTest构造方法是个实例化方法,在InitTest类被实例化时调用。
main 方法中实例化了一个InitTest类的实例。
执行顺序分析
在一个对象被使用之前,需要经历的过程有:类的装载 -> 链接(验证 -> 准备 -> 解析) -> 初始化 -> 对象实例化。这里需要注意的点主要有:
在类链接之后,类初始化之前,实际上类已经可以被实例化了。
就如此题代码中所述,在众多静态成员变量被初始化完成之前,已经有两个实例的初始化了。实际上,此时对类的实例化,除了无法正常使用类的静态成员变量以外(还没有保证完全被初始化),JVM 中已经加载了类的内存结构布局,只是没有执行初始化的过程。比如第 3 行public static InitTest t1 = new T("t1");,在链接过程中,JVM 中已经存在了一个 t1,它的值为 null,还没有执行new InitTest("t1")。又比如第 5 行的public static int i = print("i");,在没有执行初始化时,i 的值为 0.
先执行成员变量自身初始化,后执行static {…}、{…}代码块中的内容。
如此策略的意义在于让代码块能处理成员变量相关的逻辑。如果不使用这种策略,而是相反先执行代码块,那么在执行代码块的过程中,成员变量并没有意义,代码块的执行也是多余。
类实例化的过程中,先执行隐式的构造代码,再执行构造方法中的代码 这里隐式的构造代码包括了{}代码块中的代码,以及实例成员变量声明中的初始化代码,以及父类的对应的代码(本题未涉及)。为何不是先执行显示的构造方法中的代码,再执行隐式的代码呢?这也很容易解释:构造方法中可能就需要使用到实例成员变量,而这时候,我们是期待实例变量能正常使用的。
最终的结果如下:
1:j i=0 n=0
2:Creat i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:Creat i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:Static i=7 n=99
9:j i=8 n=100
10:Creat i=9 n=101
11:init i=10 n=102
标签:
原文地址:http://www.cnblogs.com/tonyluis/p/5594979.html