直接看一个例程
/*【代码1】*/ public class JvmClassTest { public static JvmClassTest OBJ = new JvmClassTest(); public static int A; public static int B = 0; //public static JvmClassTest OBJ = new JvmClassTest(); static { System.out.println("A:" + A); System.out.println("B:" + B); } public JvmClassTest() { A++; B++; } public static void main(String[] args) throws Exception { JvmClassTest.B = -1; } }
??看上去很绕,可以走读一遍自己给出一个打印结果。。。。
正确打印结果如上,这个看上去很诡异。A的值应该是int的默认值0,B显式赋值为0了,然后A++、B++,为何结果不同???稍微修改一下,将line4与line7互换一下
/*【代码2】*/ //public static JvmClassTest OBJ = new JvmClassTest(); public static int A; public static int B = 0; public static JvmClassTest OBJ = new JvmClassTest();
执行:
DBABDA610E828FE.jpg" alt="">
?如我所愿了。。。。只是调换了一下代码顺序而已,这是为什么呢?而且为什么只有B会受影响,而A不会受影响呢?解释这个问题只是需要去了解jvm在“真正执行一个java类的main()”之前,做了哪些事情?简单的说,有三件事情
也许有些书上也有类似的图,而且每个词听上去都很不具体。“加载什么”、“准备什么”。。。。这是这篇blog想搞清楚的核心问题,下面就各个击破,分别对每一步讲一些之前了解到的理解
它直接表现出来的代码应该是ClassLoader.getSystemClassLoader().loadClass("com.my.test.AbcClass")。所以具体完成类的加载工作的,是常被提到的类加载器ClassLoader,它就是专门干这件事的。“类的加载”具体而言就是指将类.class文件中的二进制数据读入到内存中将其放在方法区内,然后在堆区创建一个java.lang.Class对象,所以说“加载”的最终产出是堆中的一个Class对象,它一产生,加载这件事就干完了。这里又引出一个东东——方法区
上图截自毕玄大师的ppt,描绘了jvm内存的布局,书中都会说到方法区中存放的是类信息、类的field信息、方法信息都在其中;另外以前听到过一种说法:“堆(新生代+老生代)是留给java开发人员使用的,非堆(持久带即方法区)是留给jvm自己使用的”。再回过头看上面的描述——“类的加载”就是指将类的.class文件中的二进制数据读入到内存中将其放在方法区内,然后在堆区创建一个java.lang.Class对象,换句换说“类的加载”就是为了给程序员一个可以获得类相关定义信息的窗口,这个窗口就是Class对象,类加载的过程中将方法区的结构化类定义信息映射到堆里的一个实体Class对象中,进而程序员可以通过这道桥梁最终得到该类的一个实例,比如调用Class的newInstance()。
目前我理解类的加载时机不受程序员控制,由jvm自己控制,或许它需要考虑一些优化策略,比如对于一些jvm认为未来很可能需要用到的类,jvm可以在空闲时提前加载,即提前准备好堆中的Class对象。类加载最迟的时机应该很明确,等同于类的初始化时机,下面说初始化时会说到。
类的连接,就分开来讲它的每个子步骤吧
查了很久,没有查到,目前暂时yy认为他是跟在类加载后面的吧。。。
千辛万苦走到最后一步初始化。类的初始化可以用一句明确的话来描述——“就是按顺序把类中的static代码执行一遍”,若是对static变量赋值,则进行赋值操作(连接时只是开辟内存并给默认值,此时才给static变量赋上我们代码里写的初始值);若是static代码块,则将static块执行一遍。另外值得重点说明的是两点,初始化的时机与初始化的步骤
在以下6种场景下,才会触发“类的初始化”这件事
下图可以看出,若发生上面的6个情况中的任何1个,会触发了类的初始化,若此时该类还没有做加载&连接,会连带触发做加载&连接,但是另一种情况是在此之前jvm已经未雨绸缪地提前完成了加载&连接,这个自然是jvm设计者期望看到的情况。下图可以看出,父类有优先的初始化权利。
/*【代码3】*/ public static void main(String[] args) throws Exception { System.out.println("test"); JvmClassTest.B = -1; }执行结果如下:
/*【代码4】*/ public class JvmClassTest { //public static JvmClassTest OBJ = new JvmClassTest(); public static int A; public static int B = 0; public static JvmClassTest OBJ = new JvmClassTest(); static { System.out.println("A:" + A); System.out.println("B:" + B); } public JvmClassTest() { A++; B++; } public static void main(String[] args) throws Exception { //无 } }? 终。