类加载器

>>>>、类加载器
虚拟机规范将类加载阶段中的"通过一个类的全限定名来获取描述此类的二进制字节流",这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,这现这个动作的代码模块就是"类加载器".
类加载器是类层次划分、OSGi、热部署、代码加密等技术领域的基石.

类加载器虽然只用于实现类的加载动作,但它在java程序中起到的作用却远远不限于类加载阶段,对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性,也就是说,比较两个类是否"相等",只有在两个类是由同一个类加载器加载的前提之下才有意义,否则,即使这两个类是来源于同一个class文件,只要加载它们的类加载器不同,这两个类也不相等。
这里所指的"相等",包括代表类的class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用了instanceof关键字做对象所属关系判定的情况。

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    // ClassLoaderTest.class
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if(is == null){
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);

                } catch (Exception e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };
        Object obj = myLoader.loadClass("cn.com.sinosure.biz.express.jvm.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof cn.com.sinosure.biz.express.jvm.ClassLoaderTest);
    }
}
--------------
class cn.com.sinosure.biz.express.jvm.ClassLoaderTest
false

 

 


>>>>、类加载器分类
在java虚拟机的角度,只存在两种不同的类加载器,一种是启动类加载器Bootstrap ClassLoader,这个类加载器使用C++写成,是虚拟机自身的一部分,另一种是所有其他的类加载器,都用java语言实现独立于虚拟机之外,全都继承自抽象类java.lang.ClassLoader.
在java开发人员角度,存在三种类加载器:
1. 启动类加载器Bootstrap ClassLoader,它负责加载<JAVA_HOME>\lib目录中的,或者-Xbootclasspath参数指定的路径下的,虚拟机识别和类库,虚拟机在这些路径下仅通过文件名来识别类库,如rt.jar,如果名字不符,即使放在lib目录下也不会被加载。java程序无法直接使用这个加载器。
2. 扩展类加载器Extension ClassLoader,由sum.misc.Launcher$ExtClassLoader实现,负责加载<JAVA_HOME>\lib\ext目录中的,或者java.ext.dirs系统变量指定的路径中的类库,开发者可以直接使用扩展类加载器。
应用程序类加载器Application ClassLoader,这个类加载器由sun.misc.Launcher$AppClassLoader实现,它是java.lang.ClassLoader.getSystemClassLoader()的返回值,所以一般称它为系统类加载器,负责加载用户类路径ClassPath上指定的类库,一般情况下它是程序中默认的类加载器,开发者可以直接使用这个类加载器。


>>>>、双亲委派模型
启动类加载器Bootstrap ClassLoader
==> 扩展类加载器Extension ClassLoader
==> 应用程序类加载器Application ClassLoader
==> 自定义类加载器User ClassLoader1、User ClassLoader2
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,这里类加载器之间的父子关系一般不会通过继承关系(Inheritance)来实现,而是使用组合(Composition)关系来复用父加载器的代码。
类加载器的双亲委派模型不是一个强制性的约束模型,而是java设计者推荐的一种类加载器实现方式。
它的工作过程是,如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器,每一个层次的类加载器都如此,因此所有的加载请求最终都传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子类才尝试自己去加载。
使用双亲委派模式来组织类加载器之间的关系,一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,如java.lang.Object类,它存放在rt.jar中,无论哪一个类加载器要加载它,最终都委派给启动类加载器,因此Object在程序的各种类加载器环境中都是同一个类,相反如果没有双亲委派模式,如果用户自己写一个java.lang.Object类,放在ClassPath中,系统中就会有多个不同的Object类,Java类型体系中最基础的行为也无法保证。
有了双亲委派模式,如果用户自己写一个java.lang.Object类,将可以正常编译,但永远无法被加载运行。

posted @ 2019-09-23 17:20  LinuSiyu  阅读(149)  评论(0编辑  收藏  举报