锁定老帖子 主题:static块到底什么时候执行?
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-04-05
我倒,楼上的差点晃了我的眼睛~~
|
|
返回顶楼 | |
发表时间:2006-04-11
eway 写道 "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方法时,静态初始化块才被真正调用,应该改成-静态初始化块只是在类被第一次实体化的时候才会被仅仅调用一次。”
其实,该书作者的上述描述有误。通过一个试验,就可以看出谬误所在。 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块的绝对不是什么“只是在类被第一次实体化的时候才会被仅仅调用一次”,而应该是在类被初始化的时候,仅仅调用一次。 JDK的DOC文档是这样描述Class.forName(String name,boolean initialize,ClassLoader loader) throws ClassNotFoundException: The class is initialized only if the initialize parameter is true and if it has not been initialized earlier. 1.楼主所举例子不能说明《java深度历险》中的观点正确与否; 2.《java深度历险》所举例子中,当initialize==false时,输出: 类别准备载入 类别准备实例化 Word static initialization! 类只载入时未调用static块,而在后面的两个实例化只调用一次static块. 当initialize==true时,输出: 类别准备载入 Word static initialization! 类别准备实例化 当类载入并被实例化时,后面的两个实例化不再调用static块. 所以《java深度历险》的"静态初始化块只是在类被第一次实体化的时候才会被仅仅调用一次。"是正确的. |
|
返回顶楼 | |
发表时间:2006-04-11
怎么都觉得这是load Class时的参数决定的。
Class.forName("")跟 Class.forName(className, true, currentLoader) 是等价的。就是说一般性的load 类时就会调用static块。 但是,也允许特殊的,仅调入,但是不初始化的情况。 在newInstance前,jvm会ensure一下static块被执行。 (执行过就算,没执行就执行)。 只是普遍和特殊的差别。 《java深度历险》应该是想告诉读者特例的存在。 |
|
返回顶楼 | |
发表时间:2006-04-11
《java深度历险》想告诉读者"静态初始化块只是在类被第一次实体化的时候才会被仅仅调用一次。"。
|
|
返回顶楼 | |
发表时间:2006-04-11
robatter 写道 《java深度历险》想告诉读者"静态初始化块只是在类被第一次实体化的时候才会被仅仅调用一次。"。
这句话是有语病嘛。 import java.util.Date; public class StaticInit { private static Date initedTime; static { initedTime = new Date(); System.out.println("inited at " + initedTime ); } public static Date getTime() { return initedTime; } } import java.lang.reflect.Method; public class TestStatic { public static void main(String[] args) { Class t = null; System.out.println("before load"); try { t = Class.forName("StaticInit", false, ClassLoader.getSystemClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } if (t == null) { return; } System.out.println("before invoke"); try { Method m = t.getMethod("getTime", null); if (m == null) { return; } Object result = m.invoke(null, null); System.out.println(result.toString()); } catch (Exception e) { e.printStackTrace(); } System.out.println("after invoke"); } } 这里我可没有实例化任何东西哦。 jdk1.5.0_06 不管class load时初始化没有,invoke static 和 new instance前,jvm都要ensure这个类的initialization的。 |
|
返回顶楼 | |
发表时间:2006-04-11
请问dwangel:
实例化,初始化和initialization这三个概念之间有什么异同? |
|
返回顶楼 | |
发表时间:2006-04-11
robatter 写道 请问dwangel:
实例化,初始化和initialization这三个概念之间有什么异同? 初始化和initialization 不是一样的吗?难道中文和英文就是两个概念? ![]() 类的初始化,调用类的static 块,执行static 变量声明赋值。 类的实例化,建立这个类的instance。(instance, 大陆称为实例, 台湾好像自侯捷开始称为实体) 实例的初始化,调用这个类的constructor。 其实对于我们来说,重要的是调用类的static 方法和属性前,实例化一个类前,类的初始化工作一定会被完成。 如果你认为类的实体化是指 类的静态属性初始化完成,在内存中建立类的完整信息,那么确实那句话是对的。 我认为那句话里的类的实体化是指建立一个类的实例,那么那句话就有问题。 所以说那句话有语病啊,容易造成歧义。 |
|
返回顶楼 | |
发表时间:2006-04-11
应该改成"静态初始化块只是在类被第一次初始化的时候才会被仅仅调用一次。"。
|
|
返回顶楼 | |
发表时间:2006-04-16
其实就是类初始化的时候会执行static块,没有必要扣文咬字的,难道类就不会第二次初始化了?。
类载入后,一旦要初始化,就会执行static块,如果jvm回收了这个类的数据以后。第二次载入-->初始化时候同样会执行static块。 |
|
返回顶楼 | |
发表时间:2006-04-29
我的理解是:
在同一个JVM中,static block和static var这些的初始化只被进行一次,执行的时刻可以在静态方法被调用或类实例化. 另外我有些困惑的是,那些静态var是否会被jvm回收,如果有可能被回收,那上面我说的也许就不对了,应该改成当类没有被分配内存空间时,当使用这个类时,就会执行static block和static var初始化. 下面是第几楼的兄弟的一个例子,我修改了一下,其结果也许应证了我的想法 class StaticTest{ public StaticTest();{ System.out.println("I be initialization");; } static{ System.out.println("static initialization!");; } public static void say();{ System.out.println("Say me......");; } } class MainTest{ public static void main(String[] args);{ StaticTest.say();; StaticTest.say();; } } 运行结果如下: static initialization! Say me...... Say me...... |
|
返回顶楼 | |