首先,给大家演示两个示例代码,我们自定义一个与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不会加载用户自定义的类。

Java类加载原理中为何要设计双亲委派机制的更多相关文章

  1. java类加载过程以及双亲委派机制

    前言:最近两个月公司实行了996上班制,加上了熬了两个通宵上线,状态很不好,头疼.牙疼,一直没有时间和精力写博客,也害怕在这样的状态下写出来的东西出错.为了不让自己荒废学习的劲头和习惯,今天周日,也打 ...

  2. java安全沙箱(一)之ClassLoader双亲委派机制

    java是一种类型安全的语言,它有四类称为安全沙箱机制的安全机制来保证语言的安全性,这四类安全沙箱分别是: 类加载体系 .class文件检验器 内置于Java虚拟机(及语言)的安全特性 安全管理器及J ...

  3. JVM 专题四:类加载子系统(二)双亲委派机制

    2. 双亲委派机制 2.1 双亲委派机制工作原理 2.1.1 原理 Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存,生成class对象 ...

  4. 【JAVA开发】浅析双亲委派机制

    双亲委派机制存在的意义 双亲委派只是一种说法,个人觉得叫单亲委派更合适,因为向上传递的父类只有一个,估计只是翻译过来的,形成的一种习惯,大家可以当做单亲委派 四种加载器都是用于类的加载,只是加载的对象 ...

  5. jvm双亲委派机制详解

    双亲委派机制 ​ 记录一下JVM的双亲委派机制学习记录. 类加载器种类 ​ 当我们运行某一个java类的main方法时,首先需要由java虚拟机的类加载器将我们要执行的main方法所在的class文件 ...

  6. 浅谈JVM-类加载器结构与双亲委派机制

    一.类加载器结构 1.引导类加载器(bootstrap class loader) 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar),是用原声代码来实现的,并不继承自ja ...

  7. [五]类加载机制双亲委派机制 底层代码实现原理 源码分析 java类加载双亲委派机制是如何实现的

      Launcher启动类 本文是双亲委派机制的源码分析部分,类加载机制中的双亲委派模型对于jvm的稳定运行是非常重要的 不过源码其实比较简单,接下来简单介绍一下   我们先从启动类说起 有一个Lau ...

  8. 【转载】Java类加载原理解析

    Java类加载原理解析 原文出处:http://www.blogjava.net/zhuxing/archive/2008/08/08/220841.html 1       基本信息 摘要: 每个j ...

  9. 【Java_基础】java类加载过程与双亲委派机制

    1.类的加载.连接和初始化 当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过加载.连接.初始化三个步骤来对类进行初始化.如果没有意外,jvm将会连续完成这三个步骤,有时也把这三个步骤统称为 ...

  10. 深入理解Java类加载器(一):Java类加载原理解析

    摘要: 每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这个异常背后涉及到的是Java技术体系中的类加载机制.本文简述了JVM三种预定义类加载器,即 ...

随机推荐

  1. STM32 HAL库学习(F407ZGT6) (1)-晶振/时钟树

    时钟树(以F407为例)   对于 STM32F4 系列的芯片,正常工作的主频可以达到 168Mhz,但并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及RTC只需要几十Khz的时钟即可.同一个 ...

  2. java-树形结构数据

    在我们实际开发中会接触到树形结构,根节点子节点, 然后添加数据构成了我们的树形结构, 在Java后台利用递归思路进行构建树形结构数据,返回给前端,能以下拉菜单等形式进行展示, 以某市行政区为例 后端需 ...

  3. day107:MoFang:Python操作MongoDB数据库:PyMongo

    目录 PyMongo 1.PyMongo安装 2.数据库连接 3.数据库管理 4.集合管理 5.文档管理 PyMongo 1.PyMongo安装 pip install pymongo 2.数据库连接 ...

  4. day40:MySQL:python操作mysql:pymysql模块&SQL注入攻击

    目录 part1:用python连接mysql 1.用python连接mysql的基本语法 2.用python 创建&删除表 3.用python操作事务处理 part2:sql注入攻击 1.s ...

  5. AVL树的构建

    package com.xd.leetcode.shu; /** * created by lianzhen on 2020-03-10 10:27. describe:平衡二叉树的构建 * * LL ...

  6. 最新升级优化 shopee|美客多 Mercadolibre|shopfiy|lazada|独立货代贴单系统 可规模化的贴单打单系统 源码下载独立部署

    七想网络 跨境猴 最新优化改进版本的 虾皮代打包-虾皮代贴单 独立部署源码版本货代贴单系统 介绍: 台湾海外仓_shopee货代_虾皮物流–虾皮代贴单 虾皮代打包-虾皮代贴单-虾皮货代平台 shope ...

  7. JS执行机制--同步与异步

    单线程JavaScript语言具有单线程的特点,同一个时间只能做一件事情.这是因为JavaScript脚本语言是为了处理页面中用户的交互,以及操作DOM而诞生的.如果对某个DOM元素进行添加和删除,不 ...

  8. Karmada 多云容器编排引擎支持多调度组,助力成本优化

    摘要:Karmada 社区也在持续关注云成本的管理,在最近发布的 v1.5 版本中,支持用户在分发策略 PropagationPolicy/ClusterPropagationPolicy 中设置多个 ...

  9. 笔记:C++学习之旅---引用

    笔记:C++学习之旅---引用 什么是引用? 引用就是别名,引用并非对象,相反的,他只是为一个已经存在的对象所起的另外一个名字. /*引用就是别名*/ #include <iostream> ...

  10. h5新增特性 和 css3 新特性

    H5新增: 1)用于绘画 canvas 元素. 2) 用于媒介回放的 video 和 audio 元素. 3)语义化标签 article.footer.header.nav.section3) 4)表 ...