一、类加载器

类加载器(ClassLoader),从名称想到所包罗的意义,即加载类的东西。在大家采纳一个类在此以前,JVM必要先将此类的字节码文件(.class文件)从磁盘、网络或别的来源加载到内部存储器中,并对字节码举行深入分析生成对应的Class对象,那便是类加载器的效果。大家得以应用类加载器,完毕类的动态加载。

一、类加载器

二、类的加运载飞机制

在Java中,选用双亲委派机制来贯彻类的加载。那什么样是二老委派机制?在Java
Doc中有与上述同类一段描述:

The ClassLoader class uses a delegation model to search for classes
and resources. Each instance of ClassLoader has an associated parent
class loader. When requested to find a class or resource, a
ClassLoader instance will delegate the search for the class or
resource to its parent class loader before attempting to find the
class or resource itself. The virtual machine’s built-in class loader,
called the “bootstrap class loader”, does not itself have a parent but
may serve as the parent of a ClassLoader instance.

从上述描述中,大家得以总括出如下四点: 
1、类的加载进程接纳委托格局达成 
2、每一个 ClassLoader 都有三个父加载器。 
3、类加载器在加载类在此之前会先递归的去品尝利用父加载器加载。 
4、虚构机有多个内建的开发银行类加载器(Bootstrap ClassLoader),该加载器未有父加载器,但是足以看做另外加载器的父加载器。 
Java
提供三种类型的系统类加载器。第一种是开发银行类加载器,由C++语言完成,属于JVM的一局地,其效果是加载
/lib 目录中的文件,何况该类加载器只加载特定称谓的公文(如
rt.jar),实际不是该目录下具有的文件。别的两种是 Java
语言本人实现的类加载器,包含增加类加载器(ExtClassLoader)和应用类加载器(AppClassLoader),扩大类加载器担任加载\lib\ext目录中或体系变量
java.ext.dirs
所钦命的目录中的文件。应用程序类加载器担负加载用户类路线中的文件。用户能够平素动用扩充类加载器或系统类加载器来加载自个儿的类,可是用户不能够直接利用运转类加载器,除了这两体系加载器以外,用户也足以自定义类加载器,加载流程如下图所示: 
韦德国际1946手机版 1
小心:这里父类加载器并不是通过接二连三关系来兑现的,而是使用组合达成的。 
咱俩可以通过一段程序来表明那些进程:

/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class Test {
}

public class TestMain {
    public static void main(String[] args) {

        ClassLoader loader = Test.class.getClassLoader();
        while (loader!=null){
            System.out.println(loader);
            loader = loader.getParent();
        }
    }
}

地方程序的周转结果如下所示:

韦德国际1946手机版 2

从结果大家能够看出,私下认可景况下,用户自定义的类应用 AppClassLoader
加载,AppClassLoader 的父加载器为 ExtClassLoader,不过 ExtClassLoader
的父加载器却突显为空,那是怎样来头呢?究其缘由,运维类加载器属于 JVM
的一片段,它不是由 Java 语言达成的,在 Java
中无法直接援用,所以才再次来到空。但要是是那样,该怎么落实 ExtClassLoader 与
运维类加载器之间双亲委派机制?大家能够参见一下源码:

protected Class<?> loadClass(String name, boolean resolve)
       throws ClassNotFoundException
   {
       synchronized (getClassLoadingLock(name)) {
           // First, check if the class has already been loaded
           Class<?> c = findLoadedClass(name);
           if (c == null) {
               long t0 = System.nanoTime();
               try {
                   if (parent != null) {
                       c = parent.loadClass(name, false);
                   } else {
                       c = findBootstrapClassOrNull(name);
                   }
               } catch (ClassNotFoundException e) {
                   // ClassNotFoundException thrown if class not found
                   // from the non-null parent class loader
               }

               if (c == null) {
                   // If still not found, then invoke findClass in order
                   // to find the class.
                   long t1 = System.nanoTime();
                   c = findClass(name);

                   // this is the defining class loader; record the stats
                   sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                   sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                   sun.misc.PerfCounter.getFindClasses().increment();
               }
           }
           if (resolve) {
               resolveClass(c);
           }
           return c;
       }
   }

从源码能够看出,ExtClassLoader 和 AppClassLoader都持续自 ClassLoader
类,ClassLoader 类中经过 loadClass
方法来达成双亲委派机制。整个类的加载进度可分为如下三步:

1、查找对应的类是还是不是业已加载。 
2、若未加载,则判定当前类加载器的父加载器是或不是为空,不为空则委托给父类去加载,不然调用运行类加载器加载(findBootstrapClassOrNull
再往下会调用三个 native 方法)。 
3、若第二步加载失利,则调用当前类加载器加载。

因而地点这段程序,能够很了然的收看扩大类加载器与开发银行类加载器之间是如何实现委托格局的。

今天,大家再作证另多少个主题材料。大家将刚刚的Test类打成jar包,将其放置在 \lib\ext
目录下,然后再次运营方面包车型地铁代码,结果如下:

韦德国际1946手机版 3

后天,该类就不再通过 AppClassLoader 来加载,而是经过 ExtClassLoader
来加载了。倘若大家总计把jar包拷贝到\lib,尝试通过运维类加载器加载该类时,大家会意识编译器无法甄别该类,因为运营类加载器除了钦赐目录外,还必须是特定称谓的文件本事加载。

  类加载器(ClassLoader),看名就能够知道意思,即加载类的东西。在我们选取一个类在此以前,JVM供给先将此类的字节码文件(.class文件)从磁盘、互连网或另外来源加载到内部存款和储蓄器中,并对字节码举办解析生成对应的Class对象,这就是类加载器的功用。我们得以运用类加载器,完毕类的动态加载。

三、自定义类加载器

常见状态下,我们皆以直接接纳系统类加载器。然则,有时,大家也供给自定义类加载器。举例利用是通过网络来传输
Java
类的字节码,为保障安全性,那个字节码经过了加密管理,那时系统类加载器就不可能对其开始展览加载,这样则须求自定义类加载器来兑现。自定义类加载器一般都以三番四遍自
ClassLoader 类,从上边对 loadClass
方法来剖判来看,大家只要求运动康复核心重写
findClass 方法就可以。上面大家通过一个示范来演示自定义类加载器的流程:

package com.paddx.test.classloading;

import java.io.*;

/**
 * Created by liuxp on 16/3/12.
 */
public class MyClassLoader extends ClassLoader {

    private String root;

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] loadClassData(String className) {
        String fileName = root + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
        try {
            InputStream ins = new FileInputStream(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = 0;
            while ((length = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root;
    }

    public static void main(String[] args)  {

        MyClassLoader classLoader = new MyClassLoader();
        classLoader.setRoot("/Users/liuxp/tmp");

        Class<?> testClass = null;
        try {
            testClass = classLoader.loadClass("com.paddx.test.classloading.Test");
            Object object = testClass.newInstance();
            System.out.println(object.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

运维方面包车型大巴程序,输出结果如下:

韦德国际1946手机版 4

自定义类加载器的中坚在于对字节码文件的获得,假若是加密的字节码则要求在此类中对文件实行解密。由于这里只是演示,小编没有对class文件实行加密,由此未有解密的历程。这里有几点供给留意:

1、这里传递的文本名需如若类的全限定性名称,即com.paddx.test.classloading.Test格式的,因为
defineClass 方法是按这种格式举行拍卖的。 
2、最棒不要重写loadClass方法,因为这么轻松损坏双亲委托格局。 
3、那类 Test 类自己能够被 AppClassLoader 类加载,因而大家无法把
com/paddx/test/classloading/Test.class
放在类路线下。不然,由于老人民委员会托机制的留存,会一贯产生该类由
AppClassLoader 加载,而不会通过大家自定义类加载器来加载。

二、类的加载机制

四、总结

养父母委派机制能很好地消除类加载的统一性难题。对一个 Class
对象的话,假如类加载器不一致,即就是同一个字节码文件,生成的 Class
对象也是例外的。也正是说,类加载器也就是 Class
对象的贰个命名空间。双亲委派机制则保障了基类都由同样的类加载器加载,这样就防止了同多少个字节码文件被反复加载生成不相同的
Class 对象的主题材料。但父母委派机制只是是Java
标准所推荐的一种实现格局,它实际不是强制性的要求。近年来,非常多热铺排的手艺都已不遵循这一法规,如
OSGi 能力就动用了一种网状的组织,而非双亲委派机制。

 

豁免义务申明:本文章和音讯来源国际互连网,本网转发出于传递越来越多音讯和读书之目标。如转发稿涉及版权等主题素材,请立时联系。我们会给予更动或删除相关文章,保障你的职务。

  在Java中,采取双亲委派机制来兑现类的加载。那怎么是家长江水利委员会派机制?在Java
Doc中有诸有此类一段描述:

The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.

 从以上描述中,大家得以总计出如下四点:

1、类的加载过程使用委托情势完毕

2、各个 ClassLoader 都有二个父加载器。

3、类加载器在加载类此前会先递归的去尝试利用父加载器加载。

4、虚构机有一个内建的运转类加载器(bootstrap
ClassLoader),该加载器未有父加载器,但是足以视作任何加载器的父加载器。

   Java
提供两种等级次序的体系类加载器。第一种是开行类加载器,由C++语言完毕,属于JVM的一片段,其功能是加载
<Java_Runtime_Home>/lib
目录中的文件,并且该类加载器只加载特定称谓的公文(如
rt.jar),并非该目录下具有的文书。另外三种是 Java
语言自己实现的类加载器,包含扩充类加载器(ExtClassLoader)和应用类加载器(AppClassLoader),扩展类加载器担当加载<Java_Runtime_Home>\lib\ext目录中或系统变量
java.ext.dirs
所钦赐的目录中的文件。应用程序类加载器担负加载用户类路线中的文件。用户可以直接行使扩大类加载器或类别类加载器来加载本人的类,可是用户不大概直接利用运行类加载器,除了这两系列加载器以外,用户也能够自定义类加载器,加载流程如下图所示:

  韦德国际1946手机版 5

专注:这里父类加载器而不是因此持续关系来落实的,而是使用组合达成的。

  大家得以透过一段程序来验证这些进程:

public class Test {
}

public class TestMain {
    public static void main(String[] args) {

        ClassLoader loader = Test.class.getClassLoader();
        while (loader!=null){
            System.out.println(loader);
            loader = loader.getParent();
        }
    }
}

  下边程序的周转结果如下所示:  

韦德国际1946手机版 6

  从结果大家能够看到,默许情状下,用户自定义的类应用 AppClassLoader
加载,AppClassLoader 的父加载器为 ExtClassLoader,可是 ExtClassLoader
的父加载器却显示为空,那是怎么着来头吗?究其原因,运行类加载器属于 JVM
的一片段,它不是由 Java 语言完毕的,在 Java
中无法直接援用,所以才重回空。但如假如这么,该怎么落到实处 ExtClassLoader 与
运行类加载器之间双亲委派机制?我们可以参照他事他说加以考察一下源码:

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

  从源码能够看来,ExtClassLoader 和 AppClassLoader都三翻五次自 ClassLoader
类,ClassLoader 类中通过 loadClass
方法来贯彻双亲委派机制。整个类的加载进程可分为如下三步:

  1、查找对应的类是还是不是已经加载。

  2、若未加载,则判定当前类加载器的父加载器是不是为空,不为空则委托给父类去加载,不然调用运营类加载器加载(findBootstrapClassOrNull
再往下会调用三个 native 方法)。

  3、若第二步加载失败,则调用当前类加载器加载。

  通过上边这段程序,能够很领会的见到扩张类加载器与开发银行类加载器之间是何许贯彻委托形式的。

     
未来,我们再作证另三个主题素材。大家将刚刚的Test类打成jar包,将其放置在 <Java_Runtime_Home>\lib\ext
目录下,然后再次运营方面包车型客车代码,结果如下:

韦德国际1946手机版 7

     未来,该类就不再通过 AppClassLoader 来加载,而是经过 ExtClassLoader
来加载了。假诺大家总计把jar包拷贝到<Java_Runtime_Home>\lib,尝试通过运行类加载器加载该类时,大家会发觉编写翻译器不能甄别该类,因为运转类加载器除了内定目录外,还必须是一定称谓的文书工夫加载。

三、自定义类加载器

  日常意况下,大家都是一向运用系统类加载器。不过,不经常,大家也亟需自定义类加载器。比方采取是通过网络来传输
Java
类的字节码,为有限支撑安全性,这一个字节码经过了加密管理,那时系统类加载器就不只怕对其张开加载,那样则须求自定义类加载器来促成。自定义类加载器一般都是承袭自
ClassLoader 类,从地点对 loadClass 方法来深入分析来看,大家只须求重写
findClass 方法就能够。上边大家因此多少个演示来演示自定义类加载器的流水生产线:

  

package com.paddx.test.classloading;

import java.io.*;

/**
 * Created by liuxp on 16/3/12.
 */
public class MyClassLoader extends ClassLoader {

    private String root;

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    private byte[] loadClassData(String className) {
        String fileName = root + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
        try {
            InputStream ins = new FileInputStream(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = 0;
            while ((length = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getRoot() {
        return root;
    }

    public void setRoot(String root) {
        this.root = root;
    }

    public static void main(String[] args)  {

        MyClassLoader classLoader = new MyClassLoader();
        classLoader.setRoot("/Users/liuxp/tmp");

        Class<?> testClass = null;
        try {
            testClass = classLoader.loadClass("com.paddx.test.classloading.Test");
            Object object = testClass.newInstance();
            System.out.println(object.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

 

  运转方面包车型客车主次,输出结果如下:

韦德国际1946手机版 8  

  自定义类加载器的主导在于对字节码文件的拿走,假诺是加密的字节码则要求在此类中对文件实行解密。由于此地只是演示,作者未曾对class文件实行加密,由此尚未解密的经过。这里有几点必要专注:

  1、这里传递的公文名急需是类的全限定性名称,即com.paddx.test.classloading.Test格式的,因为
defineClass 方法是按这种格式举办管理的。

  2、最棒不用重写loadClass方法,因为这么便于破坏双亲委托格局。

  3、这类 Test 类本人能够被
AppClassLoader 类加载,由此大家不可能把
com/paddx/test/classloading/Test.class
放在类路线下。不然,由于父母委托机制的存在,会间接导致该类由
AppClassLoader 加载,而不会经过大家自定义类加载器来加载。

  四、总结

  双亲委派机制能很好地化解类加载的统一性难题。对二个 Class
对象的话,如若类加载器区别,即就是同三个字节码文件,生成的 Class
对象也是见仁见智的。也等于说,类加载器也就是 Class
对象的七个命名空间。双亲委派机制则保障了基类都由同样的类加载器加载,那样就制止了同二个字节码文件被频仍加载生成不一致的
Class 对象的难题。但家长委派机制只是是Java
标准所推荐的一种达成方式,它并非强制性的渴求。近些日子,比较多热陈设的技艺都已不遵守这一法规,如
OSGi 技巧就使用了一种网状的布局,而非双亲委派机制。

相关文章