zoukankan      html  css  js  c++  java
  • 类加载器重要方法详解

    在上次【https://www.cnblogs.com/webor2006/p/9095985.html】自定义了第一个类加载器,回顾一下:

    package com.jvm.classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    public class MyTest16 extends ClassLoader {
        private String classLoaderName;
        private final String fileExtension = ".class";//要加载的字节码文件的扩展名
    
        public MyTest16(String classLoaderName) {
            super();//将系统类加载器当作该类加载器的父加载器
            this.classLoaderName = classLoaderName;
        }
    
        public MyTest16(ClassLoader parent, String classLoaderName) {
            super(parent);//显示指定该类加载器的父加载器
            this.classLoaderName = classLoaderName;
        }
    
        @Override
        public String toString() {
            return "[" + classLoaderName + "]";
        }
    
        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            byte[] data = this.loadClassData(className);
            return this.defineClass(className, data, 0, data.length);
        }
    
        private byte[] loadClassData(String name) {
            InputStream is = null;
            byte[] data = null;
            ByteArrayOutputStream baos = null;
    
            try {
                this.classLoaderName = this.classLoaderName.replace(".", "/");
                is = new FileInputStream(new File(name + this.fileExtension));
                baos = new ByteArrayOutputStream();
    
                int ch = 0;
    
                while (-1 != (ch = is.read())) {
                    baos.write(ch);
                }
    
                data = baos.toByteArray();
    
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                try {
                    is.close();
                    baos.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
    
    
            return data;
        }
    
        private static void test(ClassLoader classLoader) throws Exception {
            Class<?> clazz = classLoader.loadClass("com.jvm.classloader.MyTest1");
            Object object = clazz.newInstance();
    
            System.out.println(object);
        }
    
        public static void main(String[] args) throws Exception {
            MyTest16 myTest16 = new MyTest16("loader1");
            test(myTest16);
        }
    }

    其中涉及到ClassLoader的三个比较重要的方法,如标红处,所以这里对其进行进一步了解:

    findClass():

    如源码实现:

    所以说此方法在自定义类加载器时一定是需要被覆写才行,另外在自定义类时我们并未主动调用findClass()方法,而只是去loadClass()了,如下:

    而根据findClass()的这句描述来说:

    也就是findClass()方法是由loadClass()系统内部来调用的,这个调用关系需要明确一下。

    然后看一下它的具体实现,首先是获取class文件的字节数组,如下:

    然后findClass()方法返回的是一个Class对象,那字节数组如何转换成Class对象呢?这里就得用到另外一个系统的方法来进行转换啦,如下:

    所以!接下来看一下difineClass()的官方说明:

    defineClass():

    其中最后一个单词"resolved【解析】"其实是指类初始化阶段的它,如下:

    而这个ProtectionDomain其实就是为了确保返回的Class的一切信息都是正确的,最典型的比如说:加载一个类,需要确保相同包下面的其它类都要用相同的包名,这些都是在类的解析阶段要做的工作,这里有个了解既可。

    如下:

    关于ProtectionDomain跟类加载器的关系不是特别大,所以仅仅了解一下它。

    接下来了解一下方法参数的含义:

    那为什么需要指定字节的起始位置,直接整个使用字节数组不就可以了么?举个栗子:比如字节数组b的长度是100,像咱们这个例子这100全是字节码的信息,但是实际有可能只有其中80个才是类的数据,其余的20并非是类的数据,这时就需要人为去指定从哪到哪是类的数据啦,所以这就是为啥需要咱们来指定的原因。

    下面再来简单看一下它的具体实现:

     loadClass():

    关于它下次再来分析~

  • 相关阅读:
    结对编程收获
    《程序员修炼之道》读书笔记
    《梦断代码》读书笔记
    《编程珠玑》和《梦断代码》(部分) 读书笔记
    团队项目个人心得
    团队项目Alpha阶段心得感悟
    第9周读书笔记
    第8周读书笔记
    结对编程收获
    第七周读书笔记
  • 原文地址:https://www.cnblogs.com/webor2006/p/9102632.html
Copyright © 2011-2022 走看看