类型生命周期开始
装载(将该二进制形式的java类型读入java虚拟机) --> 连接(分为三个步骤,验证(确认java类型数据格式正确且适于java虚拟机使用),准备(为类型分配内存),解析(将常量池中的符号引用转换为直接引用)) --> 初始化(类型初次使用或子类被初始化)
装载
通过该类型的完全限定名,产生一个代表该类型的二进制数据流;解析二进制数据流为方法区的内部数据结构,创建一个表示该类型的java.lang.Class类的实例
连接
验证
检查魔数,确保每一个部分都在正确的位置拥有正确的长度等,确保出Object外每一个类都有一个超类;等方法,类格式正确
准备
解析
在类型的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引用的过程
初始化
先初始化父类,然后执行类初始化方法,执行类变量初始化语句和类型的静态初始化语句,这两个语句被java编译器收集放到一个特殊的方法中,成为类初始化方法<clinit>,只能被虚拟机调用。如果不存在类变量和静态初始化语句或者仅包换static final变量的初始化语句(编译时常量表达式)或者存在类变量但是没有使用静态初始化语句,都不会产生<clinit>,首次使用时也不会执行类初始化。
class Dog{ static final String greeting = "world"; static{ System.out.println("hello'); } } Class Example{ public static void main(String[] args){ System.out.println(Dog.greeting); } }
输出: world
说明类Dog并没有执行类初始化
接口中的public,static,final字段必须在字段初始化语句中初始化,如果接口包含任何在编译时被解析成为一个常量的字段初始化语句,接口就会拥有一个clinit方法
interface Examplelf{ int ketchup=5; int ustard=(int)(Math.ramdom()*0.5); }
java编译器会为上述interface生成一个clinit方法,而只有ustard在clinit中初始化,因为ketchup被初始化为一个编译时常量,而Examplelf.ustard的类型保存指向这个字段的符号引用,使用Examplelf.ketchup的类型会保存5的一份本地copy
类实例化
类构造函数
Class Example{ Example(){ this(1); //调用其他构造函数 } Example(int i){ //默认调用父类的无参数构造函数 } Example(String s){ super();//显示调用父类的构造函数 } }
垃圾收集和对象的终结
由虚拟机回收对象,如果类声明来一个finalize方法,则在释放实例内存前会执行这个终结方法,但是只会调用一次,如果对象复活之后不再被引用,终结方法不会再被调用,且终结方法抛出的任何异常会忽略。
Class example{ protected void finalize(){ } }
卸载类型
启动类装载器装载的类永远是可触及的,不会被卸载,只有用户定义的类装载起装载的类才可被虚拟机回收。