1、类加载过程
加载
读取一个class文件到内存,并将静态变量、静态代码块、常量这些数据转换成方法区的运行时数据,同时在堆上生成一个Class对象作为方法区数据的访问入口。加载过程由类加载器完成,类加载无需等到首次使用的时候才加载,JVM会预先加载某些类,包括:本地的.class文件,jar包下的.class文件,网络上的.class文件,Java源文件动态编译、加载。
链接:与加载过程交叉进行
验证:检查加载的类是否符合JVM规范;
准备:为类变量(static变量)申请空间,并设置默认值(都是在方法区进行的);
解析:将常量池的符号引用变成直接引用。
初始化
执行类构造器方法“clinit”方法,这个方法是编译器自动收集类对象(static对象)的赋值和静态代码块的语句合并生成的。当初始化一个类时,一定会先初始化其父类;虚拟机会保证多线程场景下clinit方法的正确加锁和同步。
类初始化时机:
1⃣️ 执行 new语句时; 2⃣️ 调用类的静态成员(非final类型)和静态方法时
另外注意:1⃣️常量的调用不会引发类的初始化(因为常量在编译阶段就放入常量池了);2⃣️访问静态域时,只用真正声明该域的类会被初始化,比如通过子类调用父类的静态域,只会初始化父类;3⃣️通过数组定义类引用,不会引发类的初始化。
类的初始化顺序:
父类的静态变量 -- 父类的静态代码块 -- 子类的静态变量 -- 子类的静态代码块 -- 父类的非静态变量 -- 父类的非静态代码块 -- 父类的构造方法 -- 子类的非静态变量 -- 子类的非静态代码块 -- 子类的构造方法
2、类加载器
分类
BootstrapClassLoader:引导类加载器
(1)加载Java的核心库(JAVA_HOME/jre/lib/rt.jar,sun.boot.class.path路径下的内容),是用原生代码(C语言)来实现的,并不继承自 java.lang.ClassLoader。
(2)加载扩展类和应用程序类加载器。并指定他们的父类加载器。
ExtClassLoader:扩展类加载器,加载Java的扩展库(JAVA_HOME/jre/ext/*.jar, 或java.ext.dirs路径下的内容)
AppClassLoader:应用程序类加载器,加载Java应用程序classpath下的包。
自定义类加载器:继承ClassLoader类实现。
代理机制:双亲委派模型
当某个类加载器收到加载类的请求时,会将该请求委托给父类,AppClassLoader --> ExtClassLoader -->BootstrapClassLoader,如果父类加载器不能完成该类的加载,再由子类加载器进行加载。
tomcat服务器的类加载器:每个web应用都有自己的类加载器实例,不同于双亲委派模型的加载机制,它是先尝试自己加载,自己加载不了再请求父类进行加载。