一、类的生命周期:
|<--------连接-------->|
加载-->验证-->准备-->解析-->初始化-->使用-->卸载。
加载,验证,准备,初始化,卸载的顺序是确定的。解析阶段则不一定,可能会在初始化后再开始,为了支持java语言的运行时绑定。
加载:
a、通过一个类的全限定名获取定义此类的二进制字节流。(从ZIP包读取,网络中获得,运行时计算生成(动态代理,为特定接口生成代理类的二进制字节流),
其他文件(jsp)等)。
b、将字节流所代表的静态存储结构转化为方法区的运行数据结构。
c、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
注:加载阶段可能与连接阶段的部分内容(字节码文件格式验证)交叉。
验证:
1、文件格式验证,保证字节流符合Class文件格式规范。基于二进制字节流进行的,通过后才能进入内存的方法去进行存储。
2、元数据验证,保证符合java语言规范。对类的元数据进行语义校验(数据类型),比如说是否继承了父类不允许重写的方法,继承抽象类是否实现了所有的抽象方法。
3、字节码验证,通过数据流和控制流确定程序语义是合法的,符合逻辑的。方法体的校验分析,保证类的方法在执行时不会危害虚拟机安全。
4、符号引用验证、在解析阶段发生,判断符号引用是否可用。
注:在代码已经使用和验证,可通过-Xverify:none参数关闭类验证,缩短类加载时间。
准备:
正式为类变量分配内存并设置类变量初始值的阶段,在方法区中分配内存。
注:public static int value=123; 准备阶段完成后value的值是0,而value的赋值123的操作在初始化阶段执行。
解析;
1、将常量池中的符号引用替换为直接引用的过程。
a、符号引用:一组符号描述所引用的目标,能够无歧义的定位到目标。
b、直接引用:指向目标的指针,相对偏移量和间接定位到目标的句柄。
2、只要针对:类或接口、字段、类方法、借口对象、接口方法、方法类型、方法句柄、调用点限定符。
初始化:(真正开始执行类中定义的java程序代码)
执行类构造器<clinit>()方法的过程。
a、<clinit>()方法是有编译器自动收集类中的所有类变量的复制动作和静态语句块中的语句合并产生的,顺序由语句再源文件出现的顺序决定的。
b、保证子类<clinit>()方法执行前父类的<clinit>已经执行完毕。
c、<clinit>()方法不是必须的,如果没有静态块和类变量赋值语句,编译器可以不生成<clinit>()方法。
d、接口中不能使用静态语句块,但是会有类变量的赋值语句也会有<clinit>()。但是接口和类的不同点在于接口只有在类变量被使用时父接口才会被初始化。
e、虚拟机保证<client>()方法被加锁。