深入理解JVM(三):类加载器

类加载器

  1. jvm自带的加载器
    • 根类加载器(BootStrap)

      该加载器没有父加载器,它负责加载虚拟机的核心类库,如java.lang.*等.跟类加载器从系统属性sun.boot.class.path所指定的目录中加载类库.根类加载器的实现依赖于底层操作系统,属于虚拟机的实现部分,由C++实现,并没有继承java.lang.ClassLoader

    • 扩展类加载器(Extension)

      它的父加载器为根类加载器,它从java.ex.dirs系统属性所指定的目录中加载类库,或者从JDK安装目录的jre\lib\ext子目录下加载类库,如果把用户创建的JAR文件放在这个目录下,也会自动由扩展类加载器加载.扩展类加载器是纯java类,是java.lang.ClassLoader的子类

    • 系统(应用)类加载器(System)

      它的父加载器为拓展加载器,它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类库,它是用户自定义的类加载的默认父类.系统类加载器是纯java类,是java.lang.ClassLoader的子类

  2. 用户自定义的类加载器
    • java.lang.ClassLoader的子类
    • 用户可以定制类的加载方式

JVM规范允许类加载器在预料某个类将要被使用时就于预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LingageError)

如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误

调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化

类加载器时用来把类加载到虚拟机中,从JDK1.2版本开始,类的加载过程采用双亲委派机制,这种机制能更好的保证java平台的安全.在此委托机制中,除了java虚拟机自带的跟类加载器以外,其余的类加载器都有且只有一个父加载器,当java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加在,则由父加载器完成加载任务,否则才有加载器loader1本身来加载Sample类

获取ClassLoader的途径:

  1. 获取当前类的:clazz.getClassLoader();
  2. 获取当前线程上下文的ClassLoader:Thread.currentThread().getContextClassLoader()
  3. 获取系统的ClassLoader:ClassLoader.getSystemClassLoader()
  4. 获取调用者的ClassLoader:DriverManager.getCallerClassLoader()

类加载器的命名空间:

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

在同一个命名空间中,不会出现类的全限定名完全相同的两个类

在不同的命名空间中,有可能会出现类的全限定名完全相同的两个类

public class MyTest16 extends ClassLoader {

    private String classLoaderName;

    private final String fileExtension = ".class";

    //加载类的路径
    private String path;

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

    /**
     * @param classLoaderName 类加载器的名字
     *                        <p>
     *                        * Creates a new class loader using the <tt>ClassLoader</tt> returned by
     *                        * the method {@link #getSystemClassLoader()
     *                        * <tt>getSystemClassLoader()</tt>} as the parent class loader.
     */
    public MyTest16(String classLoaderName) {

        //将系统类加载器当作该类加载器的父类加载器
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyTest16(ClassLoader parentClassLoader, String classLoaderName) {
        //显示指定该类加载器的父类加载器
        super(parentClassLoader);
        this.classLoaderName = classLoaderName;
    }

    /**
     * 根据二进制的类的名字查找类,并返回其Class对象
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("findClass invoked");
        byte[] data = this.loadClassData(className);

        //将字节数组转化为类的Class对象
        return this.defineClass(className, data, 0, data.length);
    }

    /**
     * 根据类的名字返回类数据的字节数组
     *
     * @param clasaName
     * @return
     */
    private byte[] loadClassData(String clasaName) {

        System.out.println("loadClassData invoked");
        InputStream in = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        clasaName = clasaName.replace(".", "\\");

        try {
            in = new FileInputStream(new File(this.path + clasaName + this.fileExtension));
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while (-1 != (ch = in.read())) {
                baos.write(ch);
            }

            data = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return data;
    }


    @Override
    public String toString() {
        return "[ " + this.classLoaderName + " ]";
    }


    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        MyTest16 loader1 = new MyTest16("loader1");
        //loader1.setPath("D:\\mingbyte\\jvm_study\\target\\classes");
        loader1.setPath("C:\\Users\\10025\\Desktop\\");
        Class<?> aClass = loader1.loadClass("com.jvm.class_loader.MyTest1");

        //根据类的Class对象生成一个实例
        Object o = aClass.newInstance();
        System.out.println("o 的类加载器: "+o.getClass().getClassLoader());


        System.out.println();


        MyTest16 loader2 =new MyTest16("loader2");
        loader2.setPath("C:\\Users\\10025\\Desktop\\");
        Class<?> clazz = loader2.loadClass("com.jvm.class_loader.MyTest1");
        Object o2 = clazz.newInstance();
        System.out.println("o2 的类加载器: "+o2.getClass().getClassLoader());
    }


}

输出结果:

findClass invoked
loadClassData invoked
o 的类加载器: [ loader1 ]

findClass invoked
loadClassData invoked
o2 的类加载器: [ loader2 ]

结果分析:

  1. 当项目中存在MyTest1.class文件时,会由系统类加载器进行加载
  2. 删除项目中的MyTest1.class文件,在桌面com\jvm\class_loader目录下增加MyTest1.class文件,会由我们自定义的类加载器进行加载,并且不同的类加载器都可以加载该文件,因为不同类加载器的命名空间不同
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值