概述
类加载器是JVM执行类加载机制的前提。
ClassLoader的作用:
ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例。然后交给Java虚拟机进行链接、初始化等操作。因此,ClassLoader在整个装载阶段,只能影响到类的加载,而无法通过ClassLoader去改变类的链接和初始化行为。至于它是否可以运行,则由Execution Engine决定。
1-大厂面试题
2-类加载器的分类
3-类加载器的必要性
4-命名空间
代码解释:
结果:
解释:
rootDir后面的地址是我们使用javac User.class指令生成的class文件地址,然后loader1和loader2是两个用户自定义类加载器(如果自定义的不必理解),之后使用这两个用户自定义类加载器加载同一类型的User类,获得的Class对象不是同一个,可以通过Class对象调用getClassLoader()方法获取对应的类加载器了,最后通过系统类加载器获取的Class对象也是独特的,也可以通过该Class对象获取系统类加载器
5-类加载机制的基本特征
6-类加载器之间的关系
Launcher.java类:
public class Launcher {
……
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
……
}
分析:
1、验证扩展类加载器的父类是null
先看:
var1 = Launcher.ExtClassLoader.getExtClassLoader();
获取到扩展类加载器,点击该方法往里面追溯,在找到:
return new Launcher.ExtClassLoader(var0);
我们在点击该方法往里面追溯,在找到:
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
然后点击super,往里面追溯,在找到:
public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(parent);
点击其中的parent就是null,我们点击super,往里面追溯,在找到:
protected SecureClassLoader(ClassLoader parent) {
super(parent);
点击其中的parent就是null,我们点击super,往里面追溯,在找到:
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
点击其中的parent就是null,我们点击this,往里面追溯,在找到:
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
由于parent就是null,所以扩展类加载器的父类是null,也就是引导类加载器,因此我们调用获取扩展类加载器父类的方法获得的结果是null
2、验证系统类加载器的父类是扩展类加载器
先看:
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
获取到系统类加载器,点击该方法往里面追溯,在找到:
return new Launcher.AppClassLoader(var1x, var0);
其中var0就是扩展类加载器,点击AppClassLoader,往里面追溯,在找到:
AppClassLoader(URL[] var1, ClassLoader var2) {
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
其中var2就是扩展类加载器,我们点击super,往里面追溯,在找到:
public URLClassLoader(URL[] urls, ClassLoader parent,
URLStreamHandlerFactory factory) {
super(parent);
里面的parent就是扩展类加载器,我们点击super,往里面追溯,在找到:
protected SecureClassLoader(ClassLoader parent) {
super(parent);
里面的parent就是扩展类加载器,我们点击super,往里面追溯,在找到:
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
里面的parent就是扩展类加载器,我们点击this,往里面追溯,在找到:
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
由于parent就是扩展类加载器,所以系统类加载器的父类是扩展类加载器,因此我们调用获取系统类加载器父类的方法获得的结果是扩展类加载器
3、当前线程上下文的ClassLoader就是系统类加载器
Thread.currentThread().setContextClassLoader(this.loader)就是将系统类加载器设置为当前线程的上下文加载器,所以Thread.currentThread().getContextClassLoader()获取到的就是系统类加载器
02-复习:类的加载器分类
父类加载器和子类加载器的关系:
正是由于子类加载器中包含着父类加载器的引用,所以可以通过子类加载器的方法获取对应的父类加载器
注意:
启动类加载器通过C/C++语言编写,而自定义类加载器都是由Java语言编写的,虽然扩展类加载器和应用程序类加载器是被jdk开发人员使用java语言来编写的,但是也是由java语言编写的,所以也被称为自定义类加载器
1-引导类加载器
引导类加载器需要加载的jar包文件:
执行结果:
2-扩展类加载器
无法通过扩展类加载器获得引导类加载器,因为引导类加载器是用C/C++语言编写的,所以获取的值是null
2-扩展类加载器:
3-系统类加载器
4-用户自定义类加载器
03-测试不同的类的加载器
获取当前线程上下文的ClassLoader的结果就是系统类加载器,这个可以在Launcher.java中被代码证明,即:this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);Thread.currentThread().setContextClassLoader(this.loader);
04-ClassLoader源码解析
ClassLoader的主要方法
loadClass()剖析
loadClass()方法是ClassLoader.java类中的主要方法。
分析:
假设现在需要加载User类,我们自然也知道这需要使用系统类加载器加载,接下来来到系统类加载器的loadClass()方法中,假设系统类加载器没有加载User类,同步代码块的作用在上面注释中写的很清楚,然后直接获取User类的Class对象,如果c为null,将会判断系统类加载器的父类加载器是否为空,我们知道系统类加载器的父类加载器是扩展类加载器(在Launcher.java类中验证),那么parent不为null,之后进入if判断,将会调用扩展类加载器的loadClass()方法,此方法和上面的loadClasss()方法是一样的,接下来来到扩展类加载器的loadClass()方法中的判断就不在说了,因此扩展类加载器没有加载User类,所以c是null,然后parent是null(在Launcher.java中验证,通过扩展类加载器获取到的父类加载器就是null),将会执行c=findBootstrapClassOrNull(Name),这个就是判断引导类加载器是否加载了User类,如果没有加载该类就会尝试加载User类,也就是如下代码:
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
其中checkName()方法不用管,在本例中那个不会执行,然后会执行findBottstrapClass()fhfa ,如果加载成功返回对应的Class实例,否则返回null,由于引导类加载器不会加载User类,所以本次结果肯定是null了,那回到扩展类加载器的loadClass()方法中,继续看c = findBootstrapClassOrNull(name),那c就是null了,之后便会调用c = findClass(name);,这个将会调用URLClassLoader类中的重写findClass方法,这个方法就不带大家看了,不过该方法会返回一个null值,毕竟User类不是被扩展类加载器加载的,接下来回到系统类加载器的loadClass()方法中,继续看c = parent.loadClass(name, false),由于返回值c是null,然后便会调用c = findClass(name),系统类加载器正好可以加载User类,返回一个Class对象
SecureClassLoader与URLClassLoader
ExtClassLoader与AppClassLoader
Class.forName()与ClassLoader.loadClass()
05-双亲委派模型
定义与本质
优势与劣势
破坏双亲委派机制
破坏双亲委派机制1
以上简单来说就是jdk1.2之前还没有引入双亲委派机制,所以jdk1.2之前就是破坏双亲委派机制的情况
破坏双亲委派机制2
简单来说就是线程上下文类加载器让启动类加载器和系统类加载器直接联系起来了,中间的扩展类加载器被省略了,所以这破坏了双亲委派机制,其中线程上下文类加载器就是系统类加载器,这个证明在01-概述—>载器之间的关系中有解释
破坏双亲委派机制3
热替换的实现
每次调用方法之前都要加载字节码文件,然后创建对象,我们可以把字节码文件变成最新的,那么创建的对象肯定是最新的,所以这就完成了热替换
06-沙箱安全机制
JDK1.0时期
JDK1.1时期
JDK1.2时期
JDK1.6时期
07-自定义类的加载器
实现方式
08-Java9新特性
代码:
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(ClassLoaderTest.class.getClassLoader());
System.out.println(ClassLoaderTest.class.getClassLoader().getParent());
System.out.println(ClassLoaderTest.class.getClassLoader().getParent().getParent());
//获取系统类加载器
System.out.println(ClassLoader.getSystemClassLoader());
//获取平台类加载器
System.out.println(ClassLoader.getPlatformClassLoader());
//获取类的加载器的名称
System.out.println(ClassLoaderTest.class.getClassLoader().getName());
}
}