1、触发类加载的原因(主动调用与被动调用):
六种主动调用:
1)、创建类的实例(new操作、反射、cloning、反序列化)
2)、调用类的静态方法
3)、使用或对类/接口的static属性赋值(不包括static final的与在编译期确定的常量表达式(包括常量、字符串常量))
4)、调用API中的反射方法,Class.forName()等。
5)、子类被初始化
6)、被设定为JVM启动时的启动类(含main方法的主类)
其它都为被动引用:被动引用不会触发类的初始化操作(只会加载、链接),如仅申明一个类的引用、通过数组定义引用类等。
2、类的加载的完整生命过程
加载、链接(验证、准备、解析)、初始化、使用、卸载
1)、加载
i)、java编译器加载类的二进制字节流文件(.class文件),如果该类有基类,向上一直加载到根基类(不管基类是否使用都会加载)。
ii)、将二进制字节码加载到内存,解析成方法区对应的数据结构。
iii)、在java逻辑堆中生成该类的java.lang.Class对象,作为方法区中该类的入口。
类加载器:分默认加载器和用户自定义加载器
Bootstrap ClassLoader:顶层加载器,由c++实现。负责JVM启动时加载JDK核心类库以及加载后面两个类加载器。
Extension ClassLoader:继承自ClassLoader的类,负责加载{JAVA_HOME}/jre/lib/ext目录下的所有jar包。
App ClassLoader:上面加载器的子对象,负责加载应用程序CLASSPATH目录下的class文件和jar包。
Customer ClassLoader:用户继承自ClassLoader类的自定义加载器,用来处理特殊加载需求。如Tomcat等都有自己实现的加载器。
类加载器采用双亲委托(自底向上查询)来避免重复加载类,而加载顺序却是自顶向下的。
2)、链接
i)、验证:字节码完整性、final类与方法、方法签名等的检查验证。
ii)、准备:为静态变量分配存储空间(内存单元全置0,即基本类型为默认值,引用类型为null)。
iii)、解析(这步是可选的):将常量池内的符号引用替换为直接引用。
类的加载和链接只执行一次,故static成员也只加载一次,作为类所拥有、类的所有实例共享。
3)、初始化
包括类的初始化、对象的初始化。
类的初始化:初始化静态字段(执行定义处的初始化动作)、执行静态初始化块。
对象的初始化:
如果需要创建对象,则会执行创建对象并初始化:
i)、在堆上为创建的对象分配足够的存储空间,并将存储单元清零,即基本类型为默认值,引用类型为null。
i)、如果有基类、递归初始化非静态成员变量(即执行变量定义处的初始化动作)、构造方法。
ii)、最后才初始化本类非静态成员、构造方法。
4)、使用
5)、卸载
对象的引用(栈中)在超出作用域后被系统立即回收;对象本身(堆中)被gc标记为垃圾,在gc下次执行垃圾处理时被回收。
总结:一个类最先初始化static变量和static块;
然后分配该类以及父类的成员变量的内存空间,赋初值,再调用构造方法;
在父类与子类之间,总是优先创建、初始化父类。
即:(静态变量、静态初始化块)–>(变量、初始化块)–> 构造器,其中基类总是优先于子类的。