当前位置: 代码迷 >> J2SE >> 回复“daniel_kaka (卡卡)”关于静态块加载与类实例化有关问题
  详细解决方案

回复“daniel_kaka (卡卡)”关于静态块加载与类实例化有关问题

热度:200   发布时间:2016-04-24 15:54:57.0
回复“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扩展名)