一.类加载器ClassLoader
1.全限定路径:java.lang.ClassLoader
2.ClassLoader是一个抽象类
3.只有当class文件被加载进入内存之后才能被其它的class引用.ClassLoader就起到了将class加载进入内存的角色
4.ClassLoader可以根据指定的类的名称生成对应的字节码文件,再通过字节码文件反射再封装成java.lang.Class对象
二.java类加载器分类
1.bootstrap class loader(引导类加载器)
①用于加载 Java的核心类
②底层由C++编写,并不继承java.lang.ClassLoader
③负责加载$JAVA_HOME中jre/lib/rt.jar | resources.jar....里所有的class
④代码显示bootstrap加载器加载的类文件范围
import sun.misc.Launcher;
import java.net.URL;
public class BootStrapLoaderTest {
public static void main(String[] args) {
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urLs.length; i++) {
System.out.println(urLs[i]);
}
}
}
输出结果显示:
file:/E:/Java/jdk1.8/jre/lib/resources.jar
file:/E:/Java/jdk1.8/jre/lib/rt.jar
file:/E:/Java/jdk1.8/jre/lib/sunrsasign.jar
file:/E:/Java/jdk1.8/jre/lib/jsse.jar
file:/E:/Java/jdk1.8/jre/lib/jce.jar
file:/E:/Java/jdk1.8/jre/lib/charsets.jar
file:/E:/Java/jdk1.8/jre/lib/jfr.jar
file:/E:/Java/jdk1.8/jre/classes
⑤查看bootstrap加载器加载的类文件范围的另一种方式
public class BootStrapLoaderTest {
public static void main(String[] args) {
String classPath = System.getProperty("sun.boot.class.path");
System.out.println(classPath);
}
}
输出结果显示:
E:\Java\jdk1.8\jre\lib\resources.jar;E:\Java\jdk1.8\jre\lib\rt.jar;...............
2.extensions class loader(扩展类加载器)
①负责加载Java的扩展类库
②默认加载$JAVA_HOME中jre/lib/ext/目下的所有jar
③父类加载器为null
3.system class loader(系统类加载器)
①根据Java应用的CLASSPATH来加载Java类
②Java中的应用类都是通过它来加载的
③可以通过如下方式获取:
public class BootStrapLoaderTest {
public static void main(String[] args) {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
}
}
输出结果显示:
sun.misc.Launcher$AppClassLoader@18b4aac2
④父类加载器ExtClassLoader
4.custom class loader(自定义加载器)
二.ClassLoader提供的方法
1.ClassLoader getParent()
①描述:返回父类加载器进行委派
②代码演示:
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
System.out.println(systemClassLoader.getParent());
System.out.println(systemClassLoader.getParent().getParent());
}
}
③输出结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@610455d6
null
④总结:
1>ClassLoaderTest的类加载器是AppClassLoader
2>AppClassLoader的类加器是ExtClassLoader
3>ExtClassLoader的类加器是Bootstrap ClassLoader;但是Bootstrap ClassLoader底层由C++编写,所以会打印出null
三.双亲委派模型
类加载机制主要分为:全盘负责,双亲委派,缓存机制
1.工作流程图解:
2.工作流程描述:
①CustomClassLoader接收到类加载请求
查找缓存中是否有加载过该class实例:
1>若缓存中已经存在,则返回该class实例
2>若缓存中不存在,则将请求委派给父类加载器AppClassLoader
②AppClassLoader接收到类加载请求
查找缓存中是否有加载过该class实例:
1>若缓存中已经存在,则返回该class实例
2>若缓存中不存在,则将请求委派给父类加载器ExtensionClassLoader
③ExtensionClassLoader接收到类加载请求
查找缓存中是否有加载过该class实例:
1>若缓存中已经存在,则返回该class实例
2>若缓存中不存在,则将请求委派给父类加载器BootstrapClassLoader
⑤BootstrapClassLoader接收到类加载请求
查找缓存中是否有加载过该class实例:
1>若缓存中已经存在,则返回该class实例
2>若缓存中不存在,BootstrapClassLoader在路径sun.mic.boot.class下搜索
3>若在搜索范围内有搜索到,就将搜索到的class实例返回,否则交由子类加载器ExtensionClassLoader处理
⑥ExtensionClassLoader在路径java.ext.dirs下搜索
若在搜索范围内有搜索到,就将搜索到的class实例返回,否则交由子类加载器AppClassLoader处理
⑦AppClassLoader在路径java.class.path下搜索
若在搜索范围内有搜索到,就将搜索到的class实例返回,否则交由子类加载器CustomClassLoader处理
⑧CustomClassLoader在自定义时指定的路径下搜索该class,若搜索到便返回class实例.
否则将抛出ClassNotFountException异常
四.关于类加载器双亲模式的思考
1.为什么要是用这种双亲模式呢?
①其主要目的就是避免相同的类被重复加载进入内存中而造成不必要的空间浪费.父类加载器如果已经加载过该类,那么子类就没有必要在对其进行加载.
②安全因素方面的考量,双亲模式时从下至上依次去找寻该类的迹象,,如果不使用双亲模式,那么所有的类都会在子类加载器中被加载.这里的子类加载器也包括自定义类加载器.试想一个场景,在未使用双亲模式的情况下,随便一个人定义了一个包含病毒的自定义String类动态的替换掉了原有api中定义的String类型,那么这个自定义的String类一旦被加载就会出现隐患问题.相反,如果使用双亲模式的话,即便你自定义了携带病毒的String类也不会被加载(因为String类会在更高层级的父类加载器中被加载到).
2.JVM是如何判断2个class文件是否相同的呢?
①判断两个class文件相同有两个条件:
1>两个类的类名相同
2>由同一个类加载器加载
②一个test.java文件经过javac编译后生成test.class文件.有两个类加载器ClassLoaderA&ClassLoaderB分别对其进行加载分别生成对应的java.lang.Class对象,这两个对象站在JVM的角度会被认为是两个不同的对象,但它们却是同一分字节码文件.如果将这两个Class实例生成的具体对象进行转换时,就会抛出java.lang.ClassCaseException,提示这是两个不同的类型