ClassLoader的作用:
- 负责将 Class 加载到 JVM 中
- 审查每个类由谁加载(父优先的等级加载机制), 系统内置的ClassLoader通过双亲委托来加载指定路径下的class和资源
- 将 Class 字节码重新解析成 JVM 统一要求的对象格式
- 可以自定义ClassLoader一般覆盖findClass()方法。
- ContextClassLoader与线程相关,可以获取和设置,可以绕过双亲委托的机制。
初始化阶段虚拟机规范是严格规定了如下几种情况,如果类未初始化会对类进行初始化:
1. 创建类的实例
2. 对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main 方法的那个类),虚拟机会先初始化这个主类
5. 当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化。
不会初始化的几种易搞错的情况:
1. 通过子类引用父类的静态字段,不会导致子类初始化。
2. 常量会在编译阶段会存入调用类的常量池, * 本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
特别需要指出的是,类的实例化与类的初始化是两个完全不同的概念:
- 类的实例化是指创建一个类的实例(对象)的过程;
-
类的初始化是指为类中各个类成员(被static修饰的成员变量)赋初始值的过程,是类生命周期中的一个阶段。
双亲委托:可以避免重复加载,当父亲已经加载了该类的时候,就没有必要 ClassLoader再加载一次。安全好,不会被替换。
ClassLoader使用的是双亲委托机制来搜索加载类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个组合的关系)
先检查是否已经被加载过,若没有加载则调用父加载器的loadClass() 方法,若父加载器为空,则默认使用启动类加载器作为父加载器。如果父加载器失败,再调用自己的findClass 方法进行加载,因此到这里再次证明了类加载器的过程:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 先委派给父类加载器加载
c = parent.loadClass(name, false);
} else {
//如果父加载器不存在,则委托给启动类加载器 加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 如果父类加载器无法加载,自身才尝试加载. 自定义加载的时候,由于parent == null, class文件不在启动加载器的路径里面。所以最后还是调用到findClass方法里来了。所以 自定义的时候只要重写findClass方法就可以了。
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
}
}
return c;
}
类的唯一性:
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间
自定义ClassLoader:
继承ClassLoader类,实现findClass方法。
假设我们需要一个自定义的classloader,默认加载路径为D:\lib
下的jar包和资源。
Context ClassLoader 线程上下文类加载器
contextClassLoader是Thread类的成员变量,通过setContextClassLoader()
方法设置,通过getContextClassLoader()
获得。
每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader除非子线程特别设置。
参考:
https://blog.youkuaiyun.com/u014634338/article/details/81434327
https://segmentfault.com/a/1190000013469223
https://www.cnblogs.com/faunjoe88/p/8023239.html