zoukankan      html  css  js  c++  java
  • 为什么JVM需要多种类加载器

    JVM的类加载器

    刚刚学习JVM的类加载机制的时候,会被教育说JVM的类加载机制需要不同的类加载器。过了很久之后忘记了。现在再复习一下。

    为什么需要多个类加载器?
    加载器作用是通过类名来获取二进制字节流。

    我们先抛开所有问题,从写程序的角度来讲一个程序应该具有什么?
    1、健壮性
    2、功能性
    3、鲁棒性
    4、效率性
    5、维护性
    6、可靠性(安全性)

    由此,我们对比JVM。JVM也是一个软件,也应该基本符合上述的几个特性。
    健壮性和功能性:JVM可以从不同的地方去加载class,比如文件系统,web,FTP等,这就要求JVM屏蔽底层的加载逻辑,只需要提供一个classloard()的接口就行了,客户端就可以加载类但是却不用管类加载器到底是怎么实现的。
    再说安全性:JVM得保证自有类不遭到破坏(比如java.lang包下的类,这个破坏的原理就是类对同包级别及其子包下的public类具有操作权限)。为了解决这个问题,使用双亲委派机制刚好可以解决。

    此外,如果我想在一个JVM中启动两个相同的应用的不同版本(不同版本值得是同样的全限定名的类,但是功能不一样),怎么办?假设只有一个类加载器,那么第二次加载该类会失败,第二个版本的应用还是使用了第一个版本的class,那么第二个应用就无法提供第二个版本类的功能了。所以得需要每个应用都有相互隔离的类加载器,否则第二个应用的类可能会覆盖第一个应用之前加载的类,从而造成一些意想不到的后果。

    每种加载器都有对应的层级来加载某些特定的类,来保证他们之间的安全性。

    自定义的类加载器。

    package com.stat;
    
    import java.util.Date;
    
    public class MyDate extends Date {
        @Override
        public String toString() {
            return "MyDate : 我的时间。";
        }
    }
    
    package com.stat;
    
    import java.io.*;
    
    /**
     * 自定义类加载器
     */
    public class MyClassLoader extends ClassLoader {
        String classDir;
    
        public MyClassLoader() {
        }
    
        public MyClassLoader(String classDir) {
            this.classDir = classDir;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            String classFile = classDir + "/" + name + ".class";
            System.out.println("classFile path==" + classFile);
            try {
                //这个地方我们只是简单的读取文件流的方式来获取byte数组
                //其实可以尝试将class文件加密以后 这里解密 这样就可以保证
                //这种class文件 只有你写的classloader才能读取的了。
                //其他任何classloader都读取不了 包括系统的。
                byte[] classByte = toByteArray(classFile);
                return defineClass(classByte, 0, classByte.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return super.findClass(name);
        }
    
        public static byte[] toByteArray(String filename) throws IOException, FileNotFoundException {
    
            File f = new File(filename);
            if (!f.exists()) {
                throw new FileNotFoundException(filename);
            }
    
            try (ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length())) {
                BufferedInputStream in = null;
                in = new BufferedInputStream(new FileInputStream(f));
                int bufSize = 1024;
                byte[] buffer = new byte[bufSize];
                int len = 0;
                while (-1 != (len = in.read(buffer, 0, bufSize))) {
                    bos.write(buffer, 0, len);
                }
                return bos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
                throw e;
            }
        }
    }
    
    
    package com.stat;
    
    import java.util.Date;
    
    public class MyClassTest {
        public static void main(String[] args)
        {
            try {
                //注意这个路径:是在target目录下的,是编译后的路径
                Class classDate = new MyClassLoader("D:\javaworkspace\test02\test02_module01\target\classes\com\stat").loadClass("com.stat.MyDate");
                Class classDate2 = new MyClassLoader("D:\javaworkspace\test02\test02_module01\target\classes\com\stat").loadClass("MyDate");
                Date date = (Date) classDate.newInstance();
                System.out.println("date ClassLoader:"+date.getClass().getClassLoader().getClass().getName());
                System.out.println(date);
    
                Date date2 = (Date) classDate2.newInstance();
                System.out.println("date2 ClassLoader:"+date2.getClass().getClassLoader().getClass().getName());
                System.out.println(date2);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
    
        }
    
    }
    

    输出结果如下

    classFile path==D:javaworkspace	est02	est02_module01	argetclassescomstat/MyDate.class
    date ClassLoader:sun.misc.Launcher$AppClassLoader
    MyDate : 我的时间。
    date2 ClassLoader:com.stat.MyClassLoader
    MyDate : 我的时间。
    

    可以看到date 的类加载器是AppClassLoader,而date2的类加载器是自定义的

    大家可以看到classdate和classDate2 这2个类,我们在用classLoader去加载的时候传的参数唯一的不同就是前者传入了完整的包名,而后者没有。这就导致了前者的classLoader依旧是系统自带的appclassloader 而后者才是我们自定义的classloader。 原因:

    虽然对于classDate和classDate2来说,我们手动指定了她的类加载是我们自定义的myclassloader,但是根据类加载器的规则,我们能用父亲的loadclass就肯定不会用自己的,而我们系统类加载器,AppClassLoader要想loadclass成功是需要传入完整的包名的。所以classDate的构造还是传入了完整的包名,这就是为啥classDate的加载器还是AppClassLoader,但是classDate2并没有传入完整的包名,所以AppClassLoader也是找不到这个CustomDate类的,最后只能交给MyClassLoader这个最底层的,我们自定义的classloader来load

  • 相关阅读:
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 394 字符串解码
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 392 判断子序列
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 391 完美矩形
    Java实现 LeetCode 390 消除游戏
    Java实现 LeetCode 390 消除游戏
  • 原文地址:https://www.cnblogs.com/dhu121/p/14947884.html
Copyright © 2011-2022 走看看