在上章中讲解了类文件的生成和包含的信息,但是要加载到jvm中才能启动和运行;
#、一直以为 初始化和实例化 是一个意思,今天就来甑辩一下他们是否相同?
参看:http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/
http://blog.csdn.net/moreevan/article/details/6968718
开头说到 :类的初始化和对象初始化是 JVM 管理的类型生命周期中非常重要的两个环节,[这里说的是类初始化和对象的初始化,显然这是两个不同的概念,只是他们在大多数情况下是紧密执行的];
一个类的整个生命周期如下:
在装载(loading)阶段,类装载器(Bootstrap ClassLoader 或者自定义的ClassLoader) 把编译形成的class文件载入内存;
连接(lingking)阶段又可以分为三个子步骤:验证(verification)、准备(preparation)和解析(resolution)。
验证阶段 确保java文件数据格式 的正确性,并适于JVM使用。
准备阶段 JVM为静态变量/静态块分配内存空间;
解析过程 就是在class文件的常量池中寻找类、接口、字段和方法的符号引用,当程序运行时需要使用某个符号引用可以再去解析它。
初始化(initialization),初始化的5个启动点:
1.new关键字创建实例(对象实例化会启动初始化过程)
2.反射方法实例化对象(实例化)
3.调用子类时,先初始化父类(可能不会调用其构造方法)
4.初始化主类(主类一般不会实例化)
5.其他指令或句柄如:getstatic、putstatic、invokestatic、REF_getStatic、REF_putStatic、REF_invokeStatic。
注:初始化接口并不需要初始化它的父接口。
所以jvm的启动不会主动进行类初始化,除了主类;
从前4条,可以理解,初始化和实例化是完全不同的概念,今后就不要再混淆了;
静态块 就是常说的 初始化方法:
public class Demos1 { static int a=45; static { System.out.println("i'm static block----"+a); } public static void main(String[] args) { System.out.println("i'm main funtion----"+a); } }} 输出: i'm static block----45 i'm main funtion----45
可以理解为,初始化就是为静态块、静态属性、静态方法分配空间的过程,从类的生命周期图看,他们是先于实例化(构造方法)执行的;
注:静态属性是要先于静态块执行的 储存在静态数据区,静态方法、方法也是在准备阶段存放在method area的;
所以当new一个对象实例的时候,先启动类的初始化过程(静态属性优先静态块执行),而不是先执行构造方法;
class Son{ static{ System.out.println("ok"); } static int a = 34; } public class Demos1 { public static void main(String[] args) { System.out.println(Son.a); } } 输出: ok 34
在这段代码中,调用了Son的属性,这个过程会对Son进行初始化,所以就会先执行Son的初始化过程,后返回; 所以先打印ok而不是34;
注:Son.class不会执行初始化方法;
class Father{ static { System.out.println("父类的初始化方法"); } static int b = 35; } class Son extends Father{ static int a = 34; static{ System.out.println("ok"); } } public class Demos1 { public static void main(String[] args) { System.out.println(Son.b); } } 输出: 父类的初始化方法 35
上段代码,没有输出ok,由于先要对父类进行初始化,先执行b的赋值和打印"父类的初始化方法",完成之后发现b是父类的属性,就不再需要对子类Son初始化了,等以后需要初始化Son时再初始化,如: System.out.println(Son.a);执行Son的初始化方法;
jvm运行时,初始化方法只执行一次,静态属性赋值可以执行多次;
class Father{ static { System.out.println("父类的初始化方法"); } static final int b = 35; } class Son extends Father{ static int a = 34; static{ System.out.println("ok"); } } public class Demos1 { public static void main(String[] args) { System.out.println(Son.b); } } 输出: 35
这段代码连父类的初始化方法都没有执行, 这又是为什么呢?
因为jvm将final声明的常量,放入到NonInitialization类的常量池中去了,它是属于NonInitialization的属性,与Father和Son活生生的脱离了关系,调用Son.b时指向了NonInitialization的这个属性,而不再执行类初始化方法;加上System.out.println(Son.a);可以得到验证:这一步才执行Father的初始化方法;
-XX:+TraceClassLoading