zoukankan      html  css  js  c++  java
  • 自定义类加载器实战

    本次尝试在不突破双亲委派模型的基础上,自定义类加载器,以理解java类加载的流程。

    既然不突破双亲委派,那就只重写findClass方法:

     1 package classloader;
     2 
     3 import java.io.ByteArrayOutputStream;
     4 import java.io.File;
     5 import java.io.FileInputStream;
     6 
     7 public class MyClassLoader extends ClassLoader {
     8         
     9     @Override
    10     public Class<?> findClass(String name) {
    11         FileInputStream in;
    12         try {
    13             in = new FileInputStream(
    14                     new File("/Users/cz/java/" + name.replace( '.', '/') + ".class"));
    15         
    16             ByteArrayOutputStream out = new ByteArrayOutputStream();
    17             int ch = 0;
    18             while ((ch = in.read()) != -1) {
    19                 out.write(ch);
    20             }
    21             byte[] data = out.toByteArray();
    22             return defineClass(name, data, 0, data.length);
    23         } catch (Exception e) {
    24             e.printStackTrace();
    25             return null;
    26         }
    27     }
    28     
    29     public static void main(String[] args) throws Exception {
    30         MyClassLoader myClassLoader1 = new MyClassLoader();
    31         Class<?> clazz1 = myClassLoader1.loadClass("CZ");
    32         System.out.println(clazz1.getClassLoader());
    33         
    34         MyClassLoader myClassLoader2 = new MyClassLoader();
    35         Class<?> clazz2 = myClassLoader2.loadClass("CZ");
    36         System.out.println(clazz2.getClassLoader());
    37         
    38         // 验证isInstance方法是否会比较classloader
    39         Object obj1 = clazz1.newInstance();
    40         Object obj2 = clazz2.newInstance();
    41         System.out.println(clazz1.isInstance(obj1));
    42         System.out.println(clazz1.isInstance(obj2));
    43     }
    44 }

    在不指定父类加载器的情况下,类ClassLoader默认的构造方法里将父类加载器设置为了系统类加载器(AppClassLoader)

    protected ClassLoader() {
            this(checkCreateClassLoader(), getSystemClassLoader());
        }

    所以我在findClass方法里面指定了路径,不在classpath里面,所以不会被AppClassLoader加载。

    另外,我还验证了一下Class类的isInstance方法。同样的一份class文件,被两个类加载器加载后形成了两个类型。

    下面是运行结果:

    classloader.MyClassLoader@7852e922
    classloader.MyClassLoader@70dea4e
    true
    false

    下面再试试自定义类加载器去加载classpath里面的class文件

    如果你的父类加载器是AppClassLoader的话,根据双亲委派模型,是轮不到你的。所以我这里必须指定父类加载器,而且优先级必须要高于AppClassLoader。

    package classloader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    
    public class MyClassLoader2 extends ClassLoader {
    
        public MyClassLoader2() {
            
        }
        
        public MyClassLoader2(ClassLoader parent) {
            super(parent);
        }
    
        @Override
        public Class<?> findClass(String name) {
            FileInputStream in;
            try {
                in = new FileInputStream(
                        new File("/Users/cz/myapp/workspace/JVM/bin/" +
                                name.replace( '.', '/') + ".class"));
            
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                int ch = 0;
                while ((ch = in.read()) != -1) {
                    out.write(ch);
                }
                byte[] data = out.toByteArray();
                return defineClass(name, data, 0, data.length);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        
        public static void main(String[] args) throws Exception {
            // null代表启动类加载器
            MyClassLoader2 classLoader1 = new MyClassLoader2(null);
            Class<?> clazz1 = classLoader1.loadClass("classloader.MyClassLoader2");
            System.out.println(clazz1.getClassLoader());
            
            MyClassLoader2 classLoader2 = new MyClassLoader2();
            Class<?> clazz2 = classLoader2.loadClass("classloader.MyClassLoader2");
            System.out.println(clazz2.getClassLoader());
            
            Object obj = clazz2.newInstance();
            System.out.println(clazz1.isInstance(obj));
        }
    }

    总共生成了2个类加载器,classLoader1的父类加载器是启动类加载器,classLoader2的父类加载器是系统类加载器(达不到我们的要求)。

    运行结果:

    classloader.MyClassLoader2@7852e922
    sun.misc.Launcher$AppClassLoader@2a139a55
    false
  • 相关阅读:
    易错点集合
    [NOIP2014][题解]飞扬的小鸟
    [题解]P2014 选课-树型依赖的背包
    [题解]「一本通 1.3 练习 1」埃及分数
    【NOIP2015】斗地主 题解
    NOIP2015子串题解
    单人纸牌_NOI导刊2011提高(04) [题解]
    「一本通 1.3 例 5」weight 题解(玄学搜索对象)
    NOIP2017 跳房子题解
    P1850 换教室 题解
  • 原文地址:https://www.cnblogs.com/cz123/p/6861165.html
Copyright © 2011-2022 走看看