再谈类的加载器
一、概述
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);
……
}
}
二、复习:类的加载器分类
父类加载器和子类加载器的关系:
正是由于子类加载器中包含着父类加载器的引用,所以可以通过子类加载器的方法获取对应的父类加载器
注意:
启动类加载器 通过C/C++语言 编写,而自定义类加载器 都是由Java语言 编写的,虽然扩展类加载器 和应用程序类加载器 是被jdk开发人员使用java语言 来编写的,但是也是由java语言编写的,所以也被称为自定义类加载器
1、引导类加载器
引导类加载器 加载扩展类加载器 和应用程序类加载器
引导类加载器需要加载的jar包文件:
2、扩展类加载器
无法通过扩展类加载器获得引导类加载器,因为引导类加载器是用C/C++语言编写的,所以获取的值是null
扩展类加载器:
3、系统类加载器
使用频率是最高的
4、用户自定义类加载器
三、测试不同的类的加载器
获取当前线程上下文的ClassLoader的结果就是系统类加载器,这个可以在Launcher.java中被代码证明,即:
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
Thread.currentThread().setContextClassLoader(this.loader);
代码:
结果:
四、ClassLoader源码解析
1、ClassLoader的主要方法
真正加载的其实是defineClass();
2、loadClass()剖析
loadClass()方法是ClassLoader.java类中的主要方法。
满足双亲委派机制 的代码实现就在是loadClass()实现
分析:
假设现在需要加载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(),如果加载成功返回对应的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对象
3、SecureClassLoader与URLClassLoader
URLClassLoader把findClass()进行了重写
4、ExtClassLoader与AppClassLoader
5、Class.forName()与ClassLoader.loadClass()
前者:
1、静态方法,Class.forName(),直接通过Class静态调用
2、主动调用,会调用加载类的初始化方法(static{}等…)
后者:
2、实例方法,ClassLoader.getSystemClassLoader().loadClass(),通过获取到类加载器去实例调用
2、被动调用,不会去调用加载类的初始化方法(static{}等…)
五、双亲委派模型
1、定义与本质
2、优势与劣势
3、破坏双亲委派机制
①破坏双亲委派机制1
以上简单来说就是jdk1.2之前还没有引入双亲委派机制,所以jdk1.2之前就是破坏双亲委派机制的情况
②破坏双亲委派机制2
简单来说就是线程上下文类加载器让启动类加载器和系统类加载器直接联系起来了,中间的扩展类加载器被省略了,所以这破坏了双亲委派机制,其中线程上下文类加载器就是系统类加载器,这个证明在01-概述—>载器之间的关系中有解释;
③破坏双亲委派机制3
4、热替换的实现
每次调用方法之前都要加载字节码文件,然后创建对象,我们可以把字节码文件变成最新的,那么创建的对象肯定是最新的,所以这就完成了热替换
六、沙箱安全机制
隔离执行,在箱子里执行;限制程序运行环境
1、JDK1.0时期
2、JDK1.1时期
3、JDK1.2时期
4、JDK1.6时期
七、自定义类的加载器
实现方式
八、Java9新特性
之前的URLClassLoader继承关系被改变,全都继承与BuiltinClassLoader
不同的模块,由不同的类的加载器加载
代码:
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());
}
}
|