最近在看高翔龙的《Java虚拟机精讲》,在类加载相关章节看到一段例程,很有意思,就此想到一些静态代码和构造方法的问题。
代码类似于:
public class TestClassLoad {
public static TestClassLoad obj = new TestClassLoad(); //line1
public static int value1;
public static int value2 = 0; // line2
public TestClassLoad() {
value1 = 10;
value2 = 10;
System.out.println("before value1 - " + value1);
System.out.println("before value2 - " + value2);
}
public static void main(String[] args) throws Exception {
System.out.println("after value1 - " + value1);
System.out.println("after value2 - " + value2);
}
}
这段代码的问题在于line1/line2这两行的先后顺序如果不同,会使value2的值不同。line1在前,Value2为0,反之10。原因在于类的加载过程分为加载、链接、初始化三部分,line1和line2属于初始化的逻辑,在此过程中value2被赋值两次。在初始化的时候,由于先后顺序不同,value2最终的值亦不同。从这段代码来看,更可能的意图是希望value2的值为10,但不幸的是最终为0。
由此想到,对于一个Java类,静态代码属于类加载过程处理的,是交给JVM的。构造方法服务于实例化。一个类的加载和实例化是两个截然不同的过程,可能类加载之后,永远不需要实例化。所以,我觉得比较好的编程风格应该是构造方法中处理属于对象特有的数据;而静态的部分属于类共用,最好不要放在构造方法中处理,以免出现前面这个问题。这个问题有一定的隐藏性,尤其在大的工程中,一个java类可能会被无数的人修改,并且存活相当长的时间,如果真遇到类似问题,不一定易于排查。