JAVA类加载器分析

JVM三种预定义类型类加载器:

启动(Bootstrap)类加载器:引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib 下面的类库加载到内存中,但是虚拟机出于安全等因素考虑,不会加载< Java_Runtime_Home >/lib存在的陌生类,开发者通过将要加载的非JDK自身的类放置到此目录下期待启动类加载器加载是不可能的。

标准扩展(Extension)类加载器:扩展类加载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。

系统(System)类加载器:系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。AppClassLoader是加载Classpath中配置的类库.

 

类加载双亲委派机制:

JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。关于虚拟机默认的双亲委派机制,

ClassLoader主要是通过loadClass()来加载类的。

loadClass()方法的代码:

// 首先判断该类型是否已经被加载

        Class c = findLoadedClass(name);

        if (c == null) {

            //如果没有被加载,就委托给父类加载或者委派给启动类加载器加载

            try {

                if (parent != null) {

//如果存在父类加载器,就委派给父类加载器加载

                    c = parent.loadClass(name, false);

                } else {

//如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name)

                    c = findBootstrapClass0(name);

                }

            } catch (ClassNotFoundException e) {

        // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能

                c = findClass(name);

            }

        }

通过分析上面代码,可以得出类加载器的加载顺序:

BootstrapClassLoader-----ExtensionClassLoader----AppClassLoader---自定义的类加载器

怎么知道当前类使用了什么类加载器呢?

System.out.println(this.getClass().getClassLoader());----得到当前的类加载器

 

System.out.println(this.getClass().getClassLoader().getParent());----得到当前类的父加载器

System.out.println(this.getClass().getClassLoader().getParent().getParent());----得到当前类的父加载器的父加载器

 

假如当前类的加载器为sun.misc.Launcher$AppClassLoader@197d257,那么它的父加载器则为

sun.misc.Launcher$ExtClassLoader@7259da,但是扩展类加载器的父加载器则为NULL

 

扩展类加载器的父加载器为什么会为NULL呢?

由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

 

扩展类加载器的父加载器怎么使父加载器为NULL的呢?

 扩展类加载器(ExtClassLoader)调用ClassLoader(ClassLoader parent)构造函数将父类加载器设置为null

但是我们可以分析前面的代码。。当父加载器为NULL,JAVA会调用启动类加载器(findBootstrapClass0(name)

如何实现我们的自定义类加载器呢?

实现自定义加载器其它很简单。只要实现ClassLoader的findClass()方法就行了。

大概如下:

findClass(String name){

byte[] buffer=//读取文件的字节数组

return defineClass(buffer,0,buffer.length);

}

当自定义类加载器加载的类有继承相应的类或实现相应接口的话,必须把相应的父类或接口拷贝到同一路径。因为类加载器在加载子类时,会同时加载父类。

关于类的转型问题:

由于类加载器是相互隔离的,当classLoaderA加载了A类,classLoaderB也加载了A类。这个类对象是不能互相转型的。会报ClassCastException异常。

假如自定义类加载器加载A类时,而A类又实现了接口B时,我们是可以将A类的实例转成B接口的。原因是类加载器在加载A类时,会同时加载A类的所有父类(包括接口).

JAVA类加载器采用了委托模式。当一个类对象己经被加载了,就不会在去加载该类。还是直接返回。那么如何重新加载这个类呢?

实现自定义的类加载器,当类加载完成后。调用相应方法,然后在去掉相应类加载器的引用。这样类不会在存在类加载器中了。

比如:classLoaderA=null(去掉A加载器的引用)

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值