这是我参与更文挑战的第9天,活动详情查看: 更文挑战
类加载机制
JVM使用类的流程是这样的: java文件->编译->.class文件,类加载器通过加载.class文件这样就创建了一个Class类的实例,通过这个实例我们可以完成创建对象,调用方法一系列操作。
首先需要了解下三种类加载器
类加载器
虚拟机设计团队把加载动作放到 JVM 外部实现,以便让应用程序决定如何获取所需的类, JVM 提 供了 3 种类加载器:
启动类加载器(Bootstrap ClassLoader)
- 负责加载 JAVA_HOME\lib 目录中的, 或通过-Xbootclasspath 参数指定路径中的, 且被 虚拟机认可(按文件名识别, 如 rt.jar) 的类。
- 这个类加载器使用C/C++语言实现的,嵌套在JVM内部
- 并不继承自java.lang.ClassLoader
- 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
扩展类加载器(Extension ClassLoader)
- 负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类 库。
应用程序类加载器(Application ClassLoader):
- 负责加载用户路径(classpath)上的类库。
JVM 通过双亲委派模型进行类的加载, 当然我们也可以通过继承 java.lang.ClassLoader 实现自定义的类加载器。
双亲委派机制
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。
代码
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(java.lang.String.class.getClassLoader()); //null
System.out.println("-------------------");
System.out.println(Student1.class.getClassLoader());//sun.misc.Launcher$AppClassLoader@58644d46v
System.out.println("-------------------");
//DNSNameService类位于dnsns.jar包中,它存在于jre/lib/ext目录下
System.out.println(DNSNameService.class.getClassLoader());//sun.misc.Launcher$ExtClassLoader@45ee12a7
}
}
class Student1 {
}
BootstrapClassLoader对Java不可见,所以返回了null