首页 > 编程语言 >Java类加载原理中为何要设计双亲委派机制

Java类加载原理中为何要设计双亲委派机制

时间:2023-06-16 21:46:28浏览次数:45  
标签:lang java String 自定义 name 双亲 Java 加载

首先,给大家演示两个示例代码,我们自定义一个与Java核心类库中java.lang.String类名相同的代码:

package java.lang;

/**
 * 自定义java.lang.String类
 *
 * @author 编程老司机
 * @date 2023-06-16
 */
public class String {

    static {
        System.out.println("加载自定义的String类");
    }

    public String(){
        System.out.println("初始化自定义的String对象");
    }

    public static void main(String[] args) {
        String s = new String();
    }
}

运行结果:

错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
    public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application

从运行结果我们看到,程序运行报错了。错误原因是什么呢?下面我们来分析一下:
看过我前面一篇文章《JVM源码分析:深入剖析JavaMain方法中的LoadMainClass的实现》的读者可能知道,main方法主类是由应用程序类加载器加载的。应用程序类加载默认是实现了双亲委派机制的,所以应用程序类加载器会委托父加载器引导类加载器进行java.lang.String类的加载,引导类加载器是只认识Java核心类库中的java.lang.String类,该类是不存在main方法的。所以程序就报main方法不存在的错误。

接着,我们来看下一个示例代码:

/**
 * 自定义类加载器,演示沙箱安全机制
 *
 * @author 编程老司机
 * @date 2023-06-16
 */
public class CustomClassLoaderDemo {

    /**
     * 自定义类加载器
     */
    static class CustomClassLoader extends ClassLoader {

        private String classDir;

        public CustomClassLoader(String classDir) {
            this.classDir = classDir;
        }

        /**
         * 读取类文件 
         * @param className  类名
         * @return
         * @throws IOException
         */
        private byte[] loadClassFile(String className) throws IOException {
            className = className.replaceAll("\\.", "/");
            String classFilePath = classDir + "/" + className + ".class";
            FileInputStream fis = new FileInputStream(classFilePath);
            int len = fis.available();
            byte[] data = new byte[len];
            fis.read(data);
            fis.close();
            return data;
        }

        /**
         * 重写loadClass方法,实现自己的加载逻辑,去掉其中实现双亲委派机制的几行代码:
         *                     if (parent != null) {
         *                         c = parent.loadClass(name, false);
         *                     } else {
         *                         c = findBootstrapClassOrNull(name);
         *                     }
         * @param name  类名
         * @param resolve
         * @return
         * @throws ClassNotFoundException
         */
        @Override
        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) {
                    long t0 = System.nanoTime();
                    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.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }

        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            try {
                byte[] data = loadClassFile(className);
                // 将字节数组数据转为Class对象
                return defineClass(className, data, 0, data.length);
            } catch (Exception e) {
                throw new ClassNotFoundException("类加载失败", e);
            }
        }

        public static void main(String args[]) throws Exception {
            // 初始化自定义类加载器,会调用默认构造函数,
            // 将应用程序类加载器设置为自定义加载器的父加载器
            CustomClassLoader classLoader = new CustomClassLoader("D:\\");
            // 我们已经事先编译了自定义的java.lang.String类,
            // 并将类文件放在D盘根目录下
            Class clazz = classLoader.loadClass("java.lang.String");
            // 尝试创建一个自定义的java.lang.String类对象
            Object obj = clazz.newInstance();
        }
    }
}

上述代码运行结果:

Exception in thread "main" java.lang.ClassNotFoundException: 类加载失败
            at CustomClassLoaderDemo$CustomClassLoader.findClass(CustomClassLoaderDemo.java:85)
            at CustomClassLoaderDemo$CustomClassLoader.loadClass(CustomClassLoaderDemo.java:63)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
            at CustomClassLoaderDemo$CustomClassLoader.main(CustomClassLoaderDemo.java:95)
        Caused by: java.lang.SecurityException: Prohibited package name: java.lang
            at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
            at CustomClassLoaderDemo$CustomClassLoader.findClass(CustomClassLoaderDemo.java:83)
            ... 3 more

从运行结果我们看到,程序运行报错了。错误信息是“ java.lang.SecurityException: Prohibited package name: java.lang”,意思我们触发不满足安全机制的要求,定义了禁止使用的包名java.lang,进入报错的方法,看该方法的源码,我们得知,凡是以包名java.开头的都是不允许使用的:

private ProtectionDomain preDefineClass(String name,
                                            ProtectionDomain pd)
    {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);

        // 禁止使用"java."开头的类名
        if ((name != null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }

        if (name != null) checkCerts(name, pd.getCodeSource());

        return pd;
    }

通过以上两个示例说明,为何Java要设计双亲委派机制:
1. 避免类的重复加载:当父加载器已经加载了指定类时,子加载器就不会再加载一次,保证被加载类的唯一性;
2. 沙箱安全机制:防止Java核心API库被随意篡改。通俗点说,当Java核心类库中的类(比如:java.lang.String)的类名与用户自定义类的类名相同有冲突时,Java不会加载用户自定义的类。

标签:lang,java,String,自定义,name,双亲,Java,加载
From: https://www.cnblogs.com/springmorning/p/17486543.html

相关文章

  • java课设——《RookieSuperMario》【菜鸟版超级玛丽
    项目简介:我们团队利用面向对象开发方法和Javaswing框架,对经典游戏《SuperMario》进行编写。此项目共设施三个关卡,玩家可通过键盘来控制马里奥的移动,跳跃可以顶掉砖块,下落时还可以踩死蘑菇敌人,如果马里奥最终安全到达堡垒,则通关成功。个人项目负责任务: 创建背景类(BackGroun......
  • java基于springboot+vue的网吧管理系统,附源码+数据库+论文+PPT,适合课程设计、毕业设计
    1、项目介绍随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代,网吧管理系统就是信息时代变革中的产物之一。任何系统都要遵循系统设计的基......
  • JAVA JVM 层面的锁
    JVM锁1、JAVA为了实现在多线程环境灰姑娘下的线程安全,提供了诸如synchronized,ReentrantLock等工具类来解决我们在多线程环境下的线程安全问题。synchronized锁1、上面是synchronized锁synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:修饰一个代......
  • javaScript核心知识点
      一、JavaScript简介       一、JavaScript语言的介绍:JavaScript是基于对象和原型的一种动态、弱类型的脚本语言       二、JavaScript语言的组成:JavaScript是由核心语法(ECMAScript)、文档对象模型(DOM)、浏览器对象模型(BOM)组成的       三......
  • Chrome 禁用 javascript
    步骤1.打开控制台:右键>检查2.在控制台页面快捷键ctrl+shift+p然后输入javascript找到disabledjavaScript.解除禁用: ctrl+shift+p然后输入enablejavaScript找到enablejavaScrip. ......
  • java中xml和json转换
    packagecom.lbdz.bsf.util;importnet.sf.json.JSONObject;importnet.sf.json.JSONSerializer;importnet.sf.json.xml.XMLSerializer;/***xml和json转换*/publicclassXMLUtils{/***xml转化为字符串*@paramxml*@returnstr......
  • centOS下解决java生成图片中文乱码问题
    今天测试项目上线,其中有这样一个功能点:上传ppt转为图片。但是却出现了乱码,刚开始怀疑是系统编码的问题,但是:echo$LANG发现是utf-8,好像没什么问题,继续查找程序中的日志信息,发现从ppt中取出来的内容没有乱码,查看代码发现是生成图片时使用宋体字,猜测是字体没有安装。于是通过:fc-l......
  • 2、【java程序运行监控byteman】使用示例(运行中方法耗时监控、javaagent监控、jvm监控
    (文章目录)本文介绍了byteman的其他几种应用场景及示例,比如javaagent、监控jvm、bmjava命令、如何查看运行的规则、检查规则的正确性、检查规则是否在运行中等。本文分为2个部分,即运行中方法耗时监控和其他示例。一、统计方法耗时(程序运行中)该类是实时显示控制台输入的结果以......
  • java开发手记
    1.把json数据封装成一个对象接收,需要前面加上一个注解 @RequestBosy @PutMating //修改操作publicResult update(@RequestBodyEmp emp){} 2.按住alt键+鼠标左键向下拖动,可以同时选取编辑多行 3.springboot配置文件 application.yml数据格式(值和冒号之间必须......
  • 7个必备JavaScript优化技巧,CodeGeeX 5秒搞定了!
    JavaScript,目前成了使用最广泛的编程语言。这篇文章给出的是一些JavaScript的优化技巧,这些技巧帮助开发者编写出更好的代码。当写完这些代码段之后,我突然意识到,所有的这些代码段,由于它们的常用性,非常适合用AI辅助编程工具CodeGeeX来自动生成。下载使用——CodeGeeX插件,在VSCode......