赞
踩
#1、类的生命周期 类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。 其中加载、验证、准备、初始化、和卸载这5个阶段的顺序是肯定的。而解析阶段不必定:它在某些状况下能够在初始化阶段以后再开始,这是为了支持Java的运行时绑定。
关于初始化:JVM规范明确规定,有且只有5中状况必须执行对类的初始化(加载、验证、准备天然再此以前要发生):html
遇到new、getstatic、putstatic、invokestatic,若是类没有初始化,则必须初始化,这几条指令分别是指:new新对象、读取静态变量、设置静态变量,调用静态函数。
使用java.lang.reflect包的方法对类进行反射调用时,若是类没初始化,则须要初始化
当初始化一个类时,若是发现父类没有初始化,则须要先触发父类初始化。
当虚拟机启动时,用户须要制定一个执行的主类(包含main函数的类),虚拟机会先初始化这个类。
可是用JDK1.7启的动态语言支持时,若是一个MethodHandle实例最后解析的结果是REF_getStatic、REF_putStatic、Ref_invokeStatic的方法句柄时,而且这个方法句柄所对应的类没有进行初始化,则要先触发其初始化。
注意:经过子类来引用父类的静态字段,不会致使子类初始化java
public class SuperClass{
public static int value = 123;
static{
System.out.printLn("SuperClass init!");
}
}
public class SubClass extends SuperClass{
static{
System.out.println("SubClass init!");
}
}
public class Test{
public static void main(String[] args){
System.out.println(SubClass.value);
}
}
最后只会打印:SuperClass init!
常量会在编译阶段存入调用者的常量池,本质上并无直接引用到定义常量的类,所以不会触发定义常量的类初始化程序员
public class ConstClass{
public static final String HELLO_WORLD = "hello world";
static {
System.out.println("ConstClass init!");
}
}
public class Test{
public static void main(String[] args){
System.out.print(ConstClass.HELLO_WORLD);
}
}
最后结果不会出现“ConstClass init!”,只会打印:hello world!
加载安全
加载过程主要作如下3件事数据结构
经过一个类的全限定名称来获取此类的二进制流
将这个字节流所表明的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个表明这个类的java.lang.Class对象,做为方法区这个类的各类数据访问入口。
验证函数
这个阶段主要是为了确保Class文件字节流中包含信息符合当前虚拟机的要求,而且不会出现危害虚拟机自身的安全。.net
准备code
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都在方法区中分配。首先,这个时候分配内存仅仅包括类变量(被static修饰的变量),而不包括实例变量。实例变量会在对象实例化时随着对象一块儿分配在java堆中。其次这里所说的初始值“一般状况下”是数据类型的零值,假设一个类变量定义为htm
public static int value = 123;
那变量value在准备阶段后的初始值是0,而不是123,由于尚未执行任何Java方法,而把value赋值为123是在程序编译后,存放在类构造函数< clinit >()方法中。
解析对象
解析阶段是把虚拟机中常量池的符号引用替换为直接引用的过程。
初始化
类初始化时类加载的最后一步,前面类加载过程当中,除了加载阶段用户能够经过自定义类加载器参与之外,其他动做都是虚拟机主导和控制。到了初始化阶段,才是真正执行类中定义Java程序代码。
准备阶段中,变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员经过程序制定的主观计划初始化类变量。初始化过程实际上是执行类构造器< clinit >()方法的过程。
< clinit >()方法是由编译器自动收集类中全部类变量的赋值动做和静态语句块中的语句合并产生的。收集的顺序是按照语句在源文件中出现的顺序。静态语句块中只能访问定义在静态语句块以前的变量,定义在它以后的变量能够赋值,但不能访问。以下所示:
public class Test {
static{
i = 0;//給变量赋值,能够经过编译
System.out.print(i);//这句编译器会提示:“非法向前引用”
}
static int i = 1;
}
< clinit >()方法与类构造函数(或者说实例构造器< init >())不一样,他不须要显式地调用父类构造器,虚拟机会保证子类的< clinit >()方法执行以前,父类的< clinit >()已经执行完毕。
卸载
当表明一个类的Class对象再也不被引用,即不可触及时,Class对象就会结束生命周期,该类在方法区内的数据也会被卸载,从而结束类的生命周期。
由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。
#2、加载机制
Java类的初始化
本阶段负责为类变量赋正确的初始值。(类变量即静态变量)
Java编译器把全部的类变量初始化语句和静态初始化器统统收集到方法中,该方法只能被JVM调用,专门承担初始化工做。 初始化一个类必须保证其直接超类已被初始化。
并不是全部类都拥有()方法,如下类不会拥有方法:
该类既没有声明任何类变量,也没有静态初始化语句。
该类声明了类变量,但没有使用类变量初始化语句或静态初始化语句初始化。
该类只包含静态final变量的类变量初始化语句,而且类变量初始化语句是常量表达式。
Java类初始化的时机
规范定义类的初始化时机为“initialize on first active use”,即“在首次主动使用时初始化”。装载和连接在初始化以前就要完成。
首次主动使用的情形:
建立类的新实例--new,反射,克隆或反序列化;
调用类的静态方法;
操做类和接口的静态字段;(final字段除外)
调用Java的特定的反射方法;
初始化一个类的子类;
指定一个类做为Java虚拟机启动时的初始化类(含有main方法的启动类)。
除了以上6种情形,java中类的其余使用方式都是被动使用,不会致使类的初始化。
Java对象初始化
编译器为每一个类生成至少一个实例初始化方法,即()方法。此方法与源程序里的每一个构造方法对应。若是类没有声明构造方法,则生成一个默认构造方法,该方法仅调用父类的默认构造方法,同时生成与该默认构造方法对应的()方法。 ()方法内容大概为:
调用另外一个()方法(本类的另一个()方法或父类的()方法);
初始化实例变量;
与其对应的构造方法内的字节码
Java对象初始化的时机
对象初始化又称为对象实例化。Java对象在其被建立时初始化。
有两种方式建立Java对象:一种是显示对象建立,经过new关键字来调用一个类的构造函数,经过构造函数建立一个对象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。