• Java类加载器


    一、四种类加载器

    1. 引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,如 rt.jar、charsets.jar等。
    2. 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的jar包。
    3. 应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类。
    4. 自定义加载器:负责加载用户自定义路径下的类包

    二、使用

    1.系统类加载器测试

    public class JDKClassLoader {
        public static void main(String[] args) {
            System.out.println(String.class.getClassLoader());
            System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
            System.out.println(JDKClassLoader.class.getClassLoader().getClass().getName());
            System.out.println();
            ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
            ClassLoader extClassloader = appClassLoader.getParent();
            ClassLoader bootstrapLoader = extClassloader.getParent();
            System.out.println("bootstrapLoader -> " + bootstrapLoader);
            System.out.println("extClassloader -> " + extClassloader);
            System.out.println("appClassLoader -> " + appClassLoader);
            System.out.println();
            System.out.println("bootstrapLoader加载以下文 -> ");
            URL[] urls = Launcher.getBootstrapClassPath().getURLs();
            for (int i = 0; i < urls.length; i++) {
                System.out.println(urls[i]);
            }
            System.out.println();
            System.out.println("extClassloader加载以下文件 -> ");
            System.out.println(System.getProperty("java.ext.dirs"));
            System.out.println();
            System.out.println("appClassLoader加载以下文件 -> ");
            System.out.println(System.getProperty("java.class.path"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    结果如下
    在这里插入图片描述
    2.自定义类加载器示例
          自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是 loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。
    User.java

    public class User {
        private int id;
        private String name;
    
        static {
            System.out.println("load user......");
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void sout() {
            System.out.println("自定义加载器加载类调用方法......");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    TestMyClassLoader.java

    public class TestMyClassLoader {
    
        public static void main(String args[]) throws Exception {
            //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
            MyClassLoader classLoader = new MyClassLoader("D:/test");
            Class clazz = classLoader.loadClass("com.example.compareexcel.jdk.User");
            Object obj = clazz.newInstance();
            Method method = clazz.getDeclaredMethod("sout", null);
            method.invoke(obj, null);
            System.out.println(clazz.getClassLoader().getClass().getName());
    
        }
    
        static class MyClassLoader extends ClassLoader {
            private String classPath;
            public MyClassLoader(String classPath) {
                this.classPath = classPath;
            }
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                try {
                    byte[] data = loadByte(name);
                    //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
                    return defineClass(name, data, 0, data.length);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ClassNotFoundException();
                }
            }
            private byte[] loadByte(String name) throws Exception {
                name = name.replaceAll("\\.", "/");
                FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
                int len = fis.available();
                byte[] data = new byte[len];
                fis.read(data);
                fis.close();
                return data;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    将生成的 User.class 文件拷贝到如上代码里D盘目录下,
    直接执行结果如下
    在这里插入图片描述
    可以看出使用的是应用程序类加载器,原因是双亲委派机制,自定义类加载器的父类加载器是应用程序类加载器。
    然后将项目里面的User.java和User.class文件删除,再次执行发现结果如下:
    在这里插入图片描述

    三、沙箱安全机制

    重写 loadClass 方法打破双亲委派机制

    public class BreakClassLoader {
    
        public static void main(String args[]) throws Exception {
            MyClassLoader classLoader = new MyClassLoader("D:/test");
            //尝试用自己改写类加载机制去加载自己写的java.lang.String.class
            Class clazz = classLoader.loadClass("java.lang.String");
            Object obj = clazz.newInstance();
            Method method= clazz.getDeclaredMethod("sout", null);
            method.invoke(obj, null);
            System.out.println(clazz.getClassLoader().getClass().getName());
        }
    
        static class MyClassLoader extends ClassLoader {
            private String classPath;
            public MyClassLoader(String classPath) {
                this.classPath = classPath;
            }
            private byte[] loadByte(String name) throws Exception {
                name = name.replaceAll("\\.", "/");
                FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
                int len = fis.available();
                byte[] data = new byte[len];
                fis.read(data);
                fis.close();
                return data;
            }
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                try {
                    byte[] data = loadByte(name);
                    return defineClass(name, data, 0, data.length);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ClassNotFoundException();
                }
            }
            // 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
            protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
                synchronized (getClassLoadingLock(name)) {
                    // First, check if the class has already been loaded
                    Class<?> c = findLoadedClass(name);
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);
                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                    if (resolve) {
                        resolveClass(c);
                    }
                    return c;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    执行代码结果如下
    在这里插入图片描述
    JDK核心类是不允许被外部加载

    文章仅供学习交流,侵权联系删除。

  • 相关阅读:
    小波变换学习笔记
    [C++](17)数据结构:二叉搜索树的操作与实现
    浅谈数字化工厂对于企业变革意义
    某程序员:30岁了,老婆管钱,背着我买了50万股票,亏了20w,强制她清仓后又买了36万
    ios获取视频文件路径报错NSPOSIXErrorDomain
    2022年京东新百货七夕礼遇季活动有什么亮点?
    22 VueComponent 响应式处理
    Android 查看路由表
    鸿蒙开发入门 | 开发第一个鸿蒙应用+页面跳转
    程序员画图工具
  • 原文地址:https://blog.csdn.net/qq_41610957/article/details/124958129