⛪什么时候会执行类加载?
java 类名,Java程序的入口类,需要先执行类加载再执行main方法
在程序运行时,执行静态方法调用,静态变量等操作
new对象的时候
通过反射创建一个类对象,然后就可以通过反射生成实例对象,或者调用静态方法等
❗类加载只会执行一次,当实行完类加载时,方法区会保存类的信息,堆区会保存类对象
在多线程环境下,JVM执行类加载的时候,会使用synchronized加锁来保证线程安全,因为类加载只能执行一次
🕌类加载的过程
🧬学习类加载之前先了解一下类的生命周期:
前五个步骤按顺序是类加载的过程,中间的三个步骤都属于连接,所以对于类加载来说步骤如下:
🛁加载
🛁连接:验证,准备,解析
🛁初始化
🏝️加载
❗注意:这里的加载要和类加载分清,加载只是类加载过程的一个阶段
在加载阶段时,JVM虚拟机会完成下列三个事情:
🚿通过一个类的全限定名来获取定义此类的二进制字节流
🚿将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
🚿在内存中生成一个代表这个类的java.lang.Class对象作为方法区这个类的各种数据的访问入口
💡简单说,就是加载class字节码(二进制数据)到方法区,在堆中生成了一个Class对象
🏜️连接
🪞验证
验证是连接的第一步,这一阶段是保证Class文件的字节流中包含的信息符合java虚拟机规范,保证这些信息,才不会危害虚拟机自身的安全
🧭准备
准备阶段是正式为类中定义分变量(静态变量)分配内存并设置变量的初始值
比如这样一段代码:
public static int val = 10;
它初始化为val的值为0,不是10,因为10是后来初始化才对它赋10这个值
对象初始值为null,基础数据类型的初始值为基础类型作为成员变量的默认值,比如int初始值为0,boolean初始值为false
🏗️解析
解析阶段是Java虚拟机将运行时常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程
🧯符号引用:编译的class文件中,有引用到值得对应关系,此时还没有加载得内存中,就使用“符号引用”来表示这种关系
🧯直接引用:执行类加载,把class字节码加载得内存后,内存中体现得变量到值得关系为直接引用
🏖️初始化
初始化阶段,Java虚拟机真正开始执行类中的Java代码,该阶段为执行类构造器方法的过程
静态变量在此阶段才真正的初始化赋值
🛕双亲委派模型
📚学习双亲委派机制须知道的两个小点:
🛁双亲委派模型为jdk默认类的加载机制
🛁执行类加载,是Java虚拟机通过类加载器来加载类的
🏕️什么是双亲委派模型?
如果一个类加载器收到了类加载的请求,它不会自己先尝试加载这个类,而是把这个请求委派给父类加载器来执行,而受到这个请求的父类加载器也是如此,最终这个请求会委派到最顶层的启动类加载器,只有当父加载器反馈无法加载这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载
BootStrap启动类加载器(BootStrap ClassLoader)
Ext扩展类加载器(Extension ClassLoader)
App应用/系统类加载器(Application ClassLoader)
自定义类加载器
❗从上到下对应着父到子的层级关系
类加载必须了解到的两个异常:
🪟ClassNotFoundException:类加载的时候找不到该类的class文件
🪟NoClassDefFoundError:找到类的class文件,但是类加载出错,方法区里没有类信息,堆中也没有类对象
⛺双亲委派模型的优点
☃️避免重复加载类:当一个类被父类加载器执行加载后,就不会派给子类加载器执行加载
⛄确保安全:如Object,String类等都是使用jdk提供的类而不是使用自己定义的java.lang.Object,如果黑客从网络上传过来一个java.lang.Object的二进制数据,如果没有双亲委派机制安全保证,就会出现安全隐患
使用双亲委派机制,保证加载的优先是jdk提供的类
不使用双亲委派机制,加载自定义的类,jdk也会有安全的验证