回复“daniel_kaka (卡卡)”关于静态块加载与类实例化问题
原贴主题: ^_^散分了~~顺便纠正一个容易犯的小错误^_^
原贴地址: http://community.csdn.net/Expert/TopicView.asp?id=5478850
首先声明:
因为我前几天在“考研论坛”与一群研究生争论非技术问题,但最后很多在那里的贴子
被封杀,所以我不想在CSDN争论非技术问题。
我知道你最初发贴的本意是好的,也许是我指出你错误的语气让你觉得有点刺耳,
我很抱歉,我回复别人的贴子从来不在乎别人给的分数,即使是0分贴,我有兴趣
我也会回复整整一大篇。
非技术问题没有对与错,但技术问题在遵循技术规范的前提下总有对与错,
个人技术水平总是在不断增长的,犯技术错误并不可耻,
有勇气承认技术错误更让人敬佩。
另外已经结了贴的问题,如果还想讨论,最好重新发贴,不然会很难注意到的。
继续问题的讨论:
讨论前提:我水平有限,没办法从hotspotJVM源码级别讨论问题,
只在遵循以下两个技术规范:
1.The JavaTM Virtual Machine Specification Second Edition
(http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html)
2.The JavaTM Language Specification, Third Edition,
(http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html)
并结合程序例子的方式讨论问题(其间还会涉及javac相关的东西)
你最初的观点是:
==========================================
静态块里的代码不一定是第一次被加载时执行,
确切的说应该是第一次实例化的时候执行~
==========================================
首先“类的实例化”与“类的加载、连接与初始化”完全是
两个不同的阶段,只有“类的加载、连接与初始化”都完成了
才能进行“类的实例化”。
另外“类的初始化”与“类的实例化”是完全不同的两个东西,
从javac编译后的class文件的角度来说:
“类的初始化”对应的是“ <clinit> ”这个方法
“类的实例化”对应的是“ <init> ” 这个方法
还有一点,静态块里的代码也不是在类被加载时执行,而是在类被初始化时执行的。
类的加载简单说只是把class文件加载到内存,并没有执行任何代码。
类的初始化由JVM调用“ <clinit> ”这个方法来完成。
javac会把源代码中的static非final字段赋值语句以及静态块
放入“ <clinit> ”这个方法里。
(关于javac的细节请参考:OpenJDK javac(https://openjdk.dev.java.net/)
com.sun.tools.javac.jvm.Gen类的normalizeDefs方法的源码
)
有关“类的加载、连接与初始化”的内容请参考
The JavaTM Virtual Machine Specification Second Edition
第5 Loading, Linking, and Initializing节
或参考 < <深入JAVA虚拟机> > “第7章 类型的生命周期”(P153-P176)
(讲JVM的书非常非常非常的少,我只看过这一本,而且讲得确实不错)
下面分析你所给的例子:
1,测试类:
package com.daniel.test;
public class TestStatic {
static{
System.out.println( "执行静态块! ");
}
public void print(){
System.out.println( "执行TestStatic.print()! ");
}
}
2,主类:
package com.daniel.test;
public class StaticTest {
public static void main(String[] args) throws Exception {
StaticTest st = new StaticTest();
System.out.println( "准备加载com.daniel.test.TestStatic... ");
Class clazz = st.getClass().getClassLoader().loadClass( "com.daniel.test.TestStatic ");
System.out.println( "加载com.daniel.test.TestStatic成功! ");
System.out.println( "准备实例化com.daniel.test.TestStatic... ");
TestStatic ts = (TestStatic)clazz.newInstance();
System.out.println( "实例化com.daniel.test.TestStatic成功! ");
ts.print();
}
}
------解决方案--------------------非常欣赏楼主这么认真的精神!
daniel_kaka (卡卡)的帖子我也回过,他为了支持自己的论点也给出了详细的测试代码,这种精神也非常值得钦佩!只不过他的论点有点小小的错误。那篇帖子我没有继续关注下去,不知道讨论得如何了,但是在CSDN这么讨论问题才真正有价值!
支持你们二位的行为!
BTW,似乎你们都没有用javap命令的习惯,这个命令可以查看编译后的class文件的样子。语法如下:javap -c -classname(classname为编译后的类名,不包括.class扩展名)