类加载器

1、什么是类加载器

(1)类加载器就是用来加载类的东西,把类加载到内存!类加载器也是一个类:ClassLoader
(2)java程序写好以后是以.java(文本文件)的文件存在磁盘上,然后,我们通过(bin/javac.exe)编译命令把.java文件编译成.class文件(字节码文件),并存在磁盘上。
但是程序要运行,首先一定要把.class文件加载到JVM内存中才能使用的,我们所讲的classLoader,就是负责把磁盘上的.class文件加载到JVM内存的方法区中。

2、ClassLoader层次结构

(1)根类加载器(启动类加载器)(Bootstrap)
启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将(%JAVA_HOME%\jre\lib)路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。
(2): 扩展类加载器(ExtClassLoader)
虽说能拿到,但是我们在实践中很少用到它,主要加载%JAVA_HOME%\lib\ext目录下的库类
(3): 应用类加载器(AppClassLoader)
它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径, 主要加载Classpath指定的类库。开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。它主要加载我们应用程序中的类,如Test,或者用到的第三方包,如jdbc驱动包等。
这里的父类加载器与类中继承概念要区分,它们在class定义上是没有父子关系的。

3.Class加载时调用类加载器的顺序(类加载器的委托机制)

如上面的Test.class要进行加载时,它将会启动应用类加载器进行加载Test类,但是这个应用类加载器不会真正去加载他,而是会调用看是否有父加载器,结果有,是扩展类加载器,扩展类加载器也不会直接去加载,它看自己是否有父加载器没,结果它还是有的,是根类加载器。所以这个时候根类加载器就去加载这个类,可在%JAVA_HOME%\jre\lib下,它找不到com.wangmeng.Test这个类,所以他告诉他的子类加载器,我找不到,你去加载吧,子类扩展类加载器去%JAVA_HOME%\lib\ext去找,也找不着,它告诉它的子类加载器 AppClassLoader,我找不到这个类,你去加载吧,结果AppClassLoader找到了,就加到内存中,并生成Class对象。
这个时间时候启动类加载器(应用类加载器)和实际类加载器(应用类加载器)是同一个. 这就是Java中著名的双亲委托加载机制。
双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。
假设有一个开发者自己编写了一个名为Java.lang.Object的类,想借此欺骗JVM。现在他要使用自定义ClassLoader来加载自己编写的java.lang.Object类。然而幸运的是,双亲委托模型不会让他成功。因为JVM会优先在Bootstrap ClassLoader的路径下找到java.lang.Object类,并载入它。
小例子
package java.lang;
 
public class Long {
    public static void main(String[] args) {
        System.out.println("Hi, i am here");
    }
}
原因:没有main方法是因为执行的根本不是我们自己写的类,执行的是java核心中的那个Long类,当然没有main方法了。。

4、常见问题

(1)JVM根据两个方面判断两个类是否相同
一是类的全称;另一个是类加载器.
即使类的全称相同,而使用的加载器不同那Class对象也是不同的.
(2)class文件何时被加载到内存
当我们在代码中用到该类,该类的.class文件才会被加载到内存
(3)class文件会被加载几次
一个.class文件只会被加载一次
(4)什么时候创建Class对象?只要类加载到内存马上就产生
posted on 2019-08-05 13:51  天高任鸟飞,海阔任鱼游  阅读(109)  评论(0编辑  收藏  举报