"java深度历险"一书在讲解“类装载”的一章中,举了以下的例子:
引用
Java代码
- public interface Assembly{
- public void start();;
- }
-
- public class Word implements Assembly{
- static{
- System.out.println("Word static initialization!");;
- }
-
- public void start();{
- System.out.prinlnt("Word starts");;
- }
- }
-
- public class Office{
- public static void main(String args[]); throws Exception{
- Office off = new Office();;
- System.out.println("类别准备载入");;
- Class c = Class.forName(args[0],true,off.getClass();.getClassLoader(););;
- System.out.println("类别准备实例化");;
- Object o = c.newInstance();;
- Object o2= c.newInstance();;
- }
- }
执行java Office Word,运行结果如下:
“Loaded Office”
“类别准备载入”
“Loaded Accembly”
“Loaded Word””
“Word static initialization”
“类别准备实体化”。
但是如果将Office.java中Class.forName(args[0],true,off.getClass().getClassLoader())中的true变为false,再执行java Office Word结果显示为:
“Loaded Office”
“类别准备载入”
“Loaded Accembly”
“Loaded Word””
“类别准备实体化”
“Word static initialization”。
显然两次红字部分顺序相反,及static块执行的顺序不同。此书作者提出了原因,原文:
引用
“过去很多java书上提到静态初始化(static initializion block)时,都会说静态初始化区块只是在类第一次载入的时候才会被调用仅仅一次。可是上面输出却发现即使类被载入了,其静态初始化区块也没有被调用,而是在第一次调用newInstance方法时,静态初始化块才被真正调用,应该改成-静态初始化块只是在类被第一次实体化的时候才会被仅仅调用一次。”
其实,该书作者的上述描述有误。通过一个试验,就可以看出谬误所在。
Java代码
- public class TestA{
- static{
- System.out.println("Static block executed!");;
- }
- }
-
- public class Test{
- public static void main(String args[]);{
- Test test = new Test();;
- Class.forName("TestA",true,test.getClass();.getClassLoader(););;
- }
- }
运行一下,相信大家一定可以看到,“Static block executed!”的输出。这与
引用
而是在第一次调用newInstance方法时,静态初始化块才被真正调用
的说法矛盾。
其实我想事实是这样的:
一个类的运行,JVM做会以下几件事情 1、类装载 2、链接 3、初始化 4、实例化;而初始化阶段做的事情是初始化静态变量和执行静态方法等的工作。所以,当Class.forName(args[0],true,off.getClass().getClassLoader());中的true变为false的时候,就是告诉JVM不需再load class之后进行initial的工作。这样,将initial的工作推迟到了newInstance的时候进行。所以,static块的绝对不是什么“只是在类被第一次实体化的时候才会被仅仅调用一次”,而应该是在类被初始化的时候,仅仅调用一次。
文章来源:
http://x-spirit.spaces.live.com/Blog/cns!CC0B04AE126337C0!817.entry