欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java类加载器浅析

程序员文章站 2022-07-03 09:50:43
...
转自:http://www.jfox.info/java-classloader-xq

对于类加载器原理不是很清楚,该文章可以解惑,直接分享了
Java虚拟机中可以安装多个类加载器,系统默认主要有三个类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader。当然也可以自定义类加载器,自定义的加载器必须继承ClassLoader。

类加载器也是Java类,因为其它Java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这个就是BootStrap。BootStrap它是嵌套在Java虚拟机内核中的,jvm启动,这个类就会启动,它是由c++语言编写的。

Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载。

Java代码

public class ClassLoaderTest {   
  
    public static void main(String[] args) {   
        ClassLoader loader = ClassLoaderTest.class.getClassLoader();   
  
        while (loader != null) {   
            System.out.println(loader.getClass().getName());   
            loader = loader.getParent();   
        }   
        System.out.println(loader);   
    }   
}  
public class ClassLoaderTest {

    public static void main(String[] args) {
        ClassLoader loader = ClassLoaderTest.class.getClassLoader();

        while (loader != null) {
            System.out.println(loader.getClass().getName());
            loader = loader.getParent();
        }
        System.out.println(loader);
    }
} 
将输出:

Output代码

sun.misc.Launcher$AppClassLoader  
sun.misc.Launcher$ExtClassLoader  
null //null就代表是BootStrap类加载器,该加载器是*加载器,没有父类加载器 
深入

在Java中每个类都是由某个类加载器的实体来载入的,因此在Class类的实体中,都会有字段记录载入它的类加载器的实体(当为null时,其实是指Bootstrap ClassLoader)。 在java类加载器中除了引导类加载器(既Bootstrap ClassLoader),所有的类加载器都有一个父类加载器(因为他们本身自己就是java类)。而类的加载机制是遵循一种委托模式:当类加载器有加载类的需求时,会先请求其Parent加载(依次递归),如果在其父加载器树中都没有成功加载该类,则由当前类加载器加载。

Java的类加载器分为以下几种:

(1) Bootstrap ClassLoader

Bootstrap ClassLoader用C++实现,一切的开始,是所有类加载器的最终父加载器。负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。

(2) ExtClassLoader

ExtClassLoader用java实现,是Launcher.java的内部类,编译后的名字为:Launcher$ExtClassLoader.class 。此类由Bootstrap ClassLoader加载,但由于Bootstrap ClassLoader已经脱离了java体系(c++),所以Launcher$ExtClassLoader.class的Parent(父加载器)被设置为null;它用于装载Java运行环境扩展包(jre/lib/ext)中的类,而且一旦建立其加载的路径将不再改变。

(3) AppClassLoader

AppClassLoader用java实现,也是是Launcher.java的内部类,编译后的名字为:Launcher$AppClassLoader.class 。AppClassLoader是当Bootstrap ClassLoader加载完ExtClassLoader后,再被Bootstrap ClassLoader加载。所以ExtClassLoader和AppClassLoader都是被Bootstrap ClassLoader加载,但AppClassLoader的Parent被设置为ExtClassLoader。可见Parent和由哪个类加载器来加载不一定是对应的。

这个类装载器是我们经常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得,如果程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类都会由它来装载。而它的查找区域就是我们常常说到的Classpath,一旦建立其加载路径也不再改变。

(4) ClassLoader

ClassLoader一般我们自定义的ClassLoader从ClassLoader类继承而来。比如:URLClassloader是ClassLoader的一个子类,而URLClassloader也是ExtClassLoader和AppClassLoader的父类(注意不是父加载器)。

类加载器之间的父子关系为:

Output代码



BootStrap -> ExtClassLoader -> AppClassLoader (即通常所说的System ClassLoader) 
它们的管辖范围依次是:

ExtClassLoader———->JRE/lib/ext/*.jarAppClassLoader———->CLASSPATH指定的所有jar或目录。

BootStrap------>JRE/lib/rt.jar
类加载器的委托机制

当Java虚拟机要加载一个类时,到底该派哪个类加载器去加载呢?

(1) 首先是当前线程的类加载器去加载线程中的第一个类。

(2) 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B。

(3) 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

每个类加载器加载类时,又先委托给其上级类加载器。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再去找发起者类加载器的儿子。因为没有getChlid方法,即使有,那么当有多个儿子,找哪一个呢?

那么,能不能自己写个类叫java.lang.System?

一般情况下不能,因为类加载采用委托机制,这样可以保证parent类加载器优先,也就是总是使用parent类加载器能找到的类,这样总是使用java系统提供的System。因为每个类加载器加载类时,又先委托给其上级类加载器,java.lang.System在BootStrap中最先加载。但是我们可以写一个类加载器来加载我们自己写的java.lang.System类。

当需要编写自己的类加载器时:

自定义的类加载器必须继承ClassLoader。
重写loadClass方法与findClass方法。loadClass中先调用父类的loadClass,然后调用findClass,通常情况下只覆盖findClass就可以。
重写defineClass方法。
注:自定义的类加载器通常用于解密自己写的已加密的class字节码,否则即使别人拥有该class文件也无法被系统的类加载器正常加载。