类加载器

类加载器基本职责就是根据类的二进制名(binary name)读取java编译器编译好的字节码文件(.class文件),并且转化生成一个java.lang.Class类的一个实例。这样的每个实例用来表示一个Java类,jvm就是用这些实例来生成java对象的。比如new一个String对象;反射生成一个String对象,都会用到String.class 这个java.lang.Class类的对象。基本上所有的类加载器都是java.lang.ClassLoader 类的一个实例。

类加载器分类

1、 Bootstrap ClassLoader 启动类加载器,主要加载jre/lib/rt.jar里所有的class。有C++实现,不是ClassLoader子类。

2、 Extension ClassLoader扩展类加载器,主要加载jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

3、 App ClassLoader系统类加载器,负责加载classPath中指定的jar包及目录中的class

4、 自定义加载器

加载器命名空间

每个类加载器都有自己的命名空间,命名空间由该加载器所有的父加载器所加载的类组成

在同一个命名空间中不会出现类的完整名字相同的两个类

在不同的命名空间中,有可能出现完整名字相同的两个类

若有一个类加载器能成功加载Test类,那么这个类加载器被称为定义类加载器,所有能成功返回Class对象引用的类加载器(包括定义类加载器),被称为初始类加载器

子加载器所加载的类能够访问父加载器所加载的类;父加载器所加载的类无法访问子加载器所加载的类,他们命名空间不同造成的

类加载器的父亲委托机制

 

 

 

1、 至底向上检查类是否加载

2、 至顶向下尝试加载类

好处:

  1、可以确保Java核心类库的安全

  2、可以确保Java核心类库所提供的类不会被自定义类所代替

  3、不同类加载器可以为相同名称的类创建额外的命名空间,这就相当于在Java虚拟机中创建了一个又一个相互隔离的java类空间。

类加载器路径

对于启动类加载器、扩展类加载器、系统类加载器,他们都有自己的加载路径,可以通过如下代码获得

public static void main(String[] args) {
        //bootstrap加载器加载路径
        System.out.println(System.getProperty("sun.boot.class.path"));
        //扩展类加载器加载路径,扩展类加载器只能加载jar包
        System.out.println(System.getProperty("java.ext.dirs"));
        //系统类加载器加载路径
        System.out.println(System.getProperty("java.class.path"));
}

可以通过命令 -Dsun.boot.class.path,-Djava.ext.dirs, -Djaca.class.path修改类加载器加载路径

自定义类加载器

自定义类加载器,需要继承ClassLoader类,重写findClass方法

package com.jvm;

import java.io.*;

public class MyTestClassLoad extends ClassLoader {
    public String classLoaderName = "MyTestClassLoad";
    public static final String SUFFIX = ".class";
    private String path = "";

    public MyTestClassLoad(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTestClassLoad(ClassLoader parent, String classLoaderName) {
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] datas = this.getData(name);
        return this.defineClass(name, datas, 0, datas.length);//解析转化为class对象
    }

    /**
     * 获取类的字节码数据
     * @param name
     * @return
     */
    private byte[] getData(String name) {
        name = name.replace(".", "/");
        File file = new File(path + name + SUFFIX);
        InputStream in = null;
        ByteArrayOutputStream bos = null;
        byte[] datas = null;
        try {
            in = new FileInputStream(file);
            bos = new ByteArrayOutputStream();
            int ch = 0;
            while ((ch = in.read()) != -1){
                bos.write(ch);
            }
            datas = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return datas;
    }
}
package com.jvm;

public class Test6 {
    Test6 test6;

    public void setTest6(Object test6) {
        this.test6 = (Test6) test6;
    }
}
    public static void main(String[] args) throws Exception {
        MyTestClassLoad classLoad1 = new MyTestClassLoad("loader1");
        classLoad1.setPath("C:/Users/Administrator/Desktop/test/");
        Class<?> clazz1 = classLoad1.loadClass("com.jvm.Test6");
        Object object1 = clazz1.newInstance();
        MyTestClassLoad classLoad2 = new MyTestClassLoad("loader2");
        classLoad2.setPath("C:/Users/Administrator/Desktop/test/");
        Class<?> clazz2 = classLoad2.loadClass("com.jvm.Test6");
        Object object2 = clazz2.newInstance();
        Method method = clazz1.getMethod("setTest6", Object.class);
        method.invoke(object1, object2);
    }

从项目中删除Test6,系统类加载器将不能加载Test6了,使用自定义类加载器去加载Test6,两个类加载的命名空间不同,相互不可见Test6,将产生如下异常

Exception in thread "main" java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:497)
  at com.jvm.Test6_1.main(Test6_1.java:16)
Caused by: java.lang.ClassCastException: com.jvm.Test6 cannot be cast to com.jvm.Test6
  at com.jvm.Test6.setTest6(Test6.java:7)
  ... 5 more

posted @ 2020-05-11 20:29  慢跑  阅读(221)  评论(0编辑  收藏  举报