提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
01-概述
1-大厂面试题
2-类加载器的分类
public class UserTest {
public static void main(String[] args) {
User user = new User(); //隐式加载
try {
Class clazz = Class.forName("com.atguigu.java.User"); //显式加载
ClassLoader.getSystemClassLoader().loadClass("com.atguigu.java.User");//显式加载
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
3-类加载器的必要性
4-命名空间
代码解释:
结果:
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-复习:类的加载器分类
父类加载器和子类加载器的关系:
1-引导类加载器
引导类加载器需要加载的jar包文件:
执行结果:
2-扩展类加载器
无法通过扩展类加载器获得引导类加载器,因为引导类加载器是用C/C++语言编写的,所以获取的值是null
扩展类加载器:
执行结果:
3-系统类加载器
4-用户自定义类加载器
03-测试不同的类的加载器
获取当前线程上下文的ClassLoader的结果就是系统类加载器,这个可以在Launcher.java中被代码证明,即:this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);Thread.currentThread().setContextClassLoader(this.loader);
代码:
public class ClassLoaderTest1 {
public static void main(String[] args) {
//获取系统该类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取扩展类加载器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19d
//试图获取引导类加载器:失败
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null
//###########################
try {
ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader);
//自定义的类默认使用系统类加载器
ClassLoader classLoader1 = Class.forName("com.atguigu.java.ClassLoaderTest1").getClassLoader();
System.out.println(classLoader1);
//关于数组类型的加载:使用的类的加载器与数组元素的类的加载器相同
String[] arrStr = new String[10];
System.out.println(arrStr.getClass().getClassLoader());//null:表示使用的是引导类加载器
ClassLoaderTest1[] arr1 = new ClassLoaderTest1[10];
System.out.println(arr1.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2
int[] arr2 = new int[10];
System.out.println(arr2.getClass().getClassLoader());//null:不需要类的加载器
System.out.println(Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:
04-ClassLoader源码解析
ClassLoader的主要方法
loadClass()剖析
loadClass()方法是ClassLoader.java类中的主要方法。
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 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
}
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
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
分析:
假设现在需要加载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());
}
}