原贴主题: ^_^散分了~~顺便纠正一个容易犯的小错误^_^
原贴地址: 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();
}
}
原贴地址: 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();
}
}
1: 把你的com.daniel.test.StaticTest改一下:
package com.daniel.test;
public class StaticTest {
//下面的代码是我加的
////////////////////开始////////////////////
static{
System.out.println("执行StaticTest的静态块!");
} StaticTest(){
System.out.println("实例化com.daniel.test.StaticTest成功!\r\n");
}
////////////////////结束//////////////////// 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();
*/
}
}结果:
=======================================
执行StaticTest的静态块!
=======================================说明:即使我还没开始想要对com.daniel.test.StaticTest进行实例化,
StaticTest的静态块也同样被执行了。
2: 再改成这样
package com.daniel.test;
public class StaticTest {
//下面的代码是我加的
////////////////////开始////////////////////
static{
System.out.println("执行StaticTest的静态块!");
} StaticTest(){
System.out.println("实例化com.daniel.test.StaticTest成功!\r\n");
}
////////////////////结束//////////////////// public static void main(String[] args) throws Exception {
System.out.println("准备实例化com.daniel.test.StaticTest...");//我加上的 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();
}
}结果:
=======================================
执行StaticTest的静态块!
准备实例化com.daniel.test.StaticTest...
实例化com.daniel.test.StaticTest成功!准备加载com.daniel.test.TestStatic...
加载com.daniel.test.TestStatic成功!
准备实例化com.daniel.test.TestStatic...
执行静态块!
实例化com.daniel.test.TestStatic成功!
执行TestStatic.print()!
=======================================说明:com.daniel.test.StaticTest类的静态块还是在实例化前执行,输出结果:
=======================================
“准备加载com.daniel.test.TestStatic...
加载com.daniel.test.TestStatic成功!
准备实例化com.daniel.test.TestStatic...
执行静态块!
实例化com.daniel.test.TestStatic成功!
执行TestStatic.print()!”
=======================================
也说明了“类的加载、连接(隐含的,看不见)与初始化”都完成了
才到“类的实例化”3.把com.daniel.test.TestStatic与com.daniel.test.StaticTest都改一下
package com.daniel.test;
public class TestStatic {
public static int field=10;
static{
System.out.println("通过引用静态字段执行TestStatic静态块!");
}
}package com.daniel.test;
public class StaticTest {
public static void main(String[] args) throws Exception {
System.out.println("TestStatic.field="+TestStatic.field);
}
}结果:
=======================================
通过引用静态字段执行TestStatic静态块!
TestStatic.field=10
=======================================
3.把com.daniel.test.TestStatic与com.daniel.test.StaticTest再改一下
package com.daniel.test;
public class TestStatic {
public static void print() {
System.out.println("调用TestStatic类的静态方法print()!");
}
static{
System.out.println("通过调用静态方法执行TestStatic静态块!");
}
}package com.daniel.test;
public class StaticTest {
public static void main(String[] args) throws Exception {
TestStatic.print();
}
}结果:
=======================================
通过调用静态方法执行TestStatic静态块!
调用TestStatic类的静态方法print()!
=======================================
回答你下面的话:
=========================================================================
to : KRplusSRequalGOD(狂人+善人=神) ( ) 信誉:100 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();
TestStatic.printStatic();//这是我加的
}
}试试上面代码~~~
你是人为的把TestStatic.printStatic(),提到前面执行,
你如果把它写到后面呢,你可以自己看看结果~
即过很明显,static块仍就不是先执行的~~
你的说法不正确呀~~~
因为按照你的说法,
TestStatic.printStatic()不管放到哪,static都要先执行才对!另外还有用
public static int a=10;
这一类的来说明load class的时候就执行静态块,
但你们用的例子也是把System.out.println(StaticTest.a);
放到load class的前面执行的,犯了上面同样的错误!按照你们的说法,System.out.println(StaticTest.a);
不管放到那都要先执行static块的代码的,
但是你们测试了把这段代码放到后面执行的结果了吗!?
=========================================================================请参考
The JavaTM Virtual Machine Specification Second Edition
第5.5 Initialization节
或参考<<深入JAVA虚拟机>>第154页,从你的回复中看出你还没有弄清楚类初始化是在什么时候进行的?
同一个类只被相关的ClassLoader加载一次,静态块只会执行一次。
调用TestStatic.printStatic()或StaticTest.a前
会先要求把TestStatic,StaticTest类加载、连接与初始化了,
你再st.getClass().getClassLoader().loadClass实际上是多余的,
根本不会再加载一次,静态块也不会再执行一次。当然,如果是先st.getClass().getClassLoader().loadClass,再newInstance(),
此时静态块肯定执行过一次了,当再调用TestStatic.printStatic()或StaticTest.a,时,也不会
再把TestStatic,StaticTest类加载一次,静态块也不会再执行一次。
================
不过也该结贴了~~~从这些可以看出来,一些人的认识实在是太肤浅了~~~
不排除大多数是新手~~有的人只知道书上写的,从不动手去写,但他们哪里知道书上的并不都是对的~~
遇到问题自己去写出来,用事实证明这才是道理!!~
不管对错,能拿出例子来,不能只凭一句xxxx书上写的清楚~~拿程序来说话才是做程序员的素质要求!
=====================我不再谈论非技术问题,谢谢!
daniel_kaka (卡卡)的帖子我也回过,他为了支持自己的论点也给出了详细的测试代码,这种精神也非常值得钦佩!只不过他的论点有点小小的错误。那篇帖子我没有继续关注下去,不知道讨论得如何了,但是在CSDN这么讨论问题才真正有价值!
支持你们二位的行为!BTW,似乎你们都没有用javap命令的习惯,这个命令可以查看编译后的class文件的样子。语法如下:javap -c -classname(classname为编译后的类名,不包括.class扩展名)