来自:一汪清水 | 责编:乐乐

链接:blog.csdn.net/tang9140/article/details/42738433

   正文   

最近学习了下java类加载相关的知识。然后看到网上有一道面试题是

能不能自己写个类叫java.lang.System?

网上提供的答案:通常不可以,但可以采取另类方法达到这个需求。所谓的另类方法指自己写个类加载器来加载java.lang.System达到目的。

首先表明下我的观点。上述答案完全是误导读者,是不正确的答案。我就纳闷了网上怎么把这种完全不正确的搜索结果排在前面,而且几乎搜到的都是这种不正确的答案。可能很多不明真相的朋友就这么被误导了,所以还是希望大家对网上的内容先持怀疑态度为好。下面详细说明为什么。

首先,摘抄网上错误答案的详细解释

“为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。

但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。”

然后,说明下上面解释中提到的一些概念

类加载器可分为两类:一是启动类加载器(Bootstrap ClassLoader),是C++实现的,是JVM的一部分;另一种是其它的类加载器,是Java实现的,独立于JVM,全部都继承自抽象类java.lang.ClassLoader。jdk自带了三种类加载器,分别是启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension ClassLoader),应用程序类加载器(Application ClassLoader)。后两种加载器是继承自抽象类java.lang.ClassLoader。关于这三种加载器各自的作用这里不做详细说明,有兴趣的可以自己了解下。

类加载器是有层次的

一般是:自定义类加载器  >> 应用程序类加载器  >> 扩展类加载器  >> 启动类加载器

上面的层次关系被称为双亲委派模型(Parents Delegation Model)。除了最顶层的启动类加载器外,其余的类加载器都有对应的父类加载器。

再简单说下双亲委托机制:如果一个类加载器收到了类加载的请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是加此,因此所有的加载请求最终到达顶层的启动类加载器,只有当父类加载器反馈自己无法完成加载请求时(指它的搜索范围没有找到所需的类),子类加载器才会尝试自己去加载。

再回去看下解释内容,我相信前面的部分大家应该很看懂了,也没什么大问题。最后的如果部分“如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。” 我就不明白所以了,逻辑完全不通。我想它的本意可能是,将自己的java.lang.System类放置在特殊目录,然后系统自带的加载器无法加载,这样最终还是由我们自己的加载器加载(因为我们自己的加载器知道其所在的特殊目录)。这种说法好像逻辑上没有问题,那么我们就来实验下了。

代码验证

测试类结构及内容如下:

public class MyClassLoader extends ClassLoader{

public MyClassLoader() {        super(null);    }

    @Overridepublic Class> loadClass(String name) throws ClassNotFoundException {try{            String className = null;if(name.startsWith("java.lang")){                className = "/" + name.replace('.', '/') + ".class";            }else{                className = name.substring(name.lastIndexOf('.') + 1) + ".class";            }            System.out.println(className);            InputStream is = getClass().getResourceAsStream(className);            System.out.println(is);if(is == null)return super.loadClass(name);

            byte[] b = new byte[is.available()];            is.read(b);return defineClass(name, b, 0, b.length);        }catch (Exception e) {            e.printStackTrace();throw new ClassNotFoundException();        }    }}
public class ClassLoaderTest {

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {        ClassLoader myLoader = new MyClassLoader();        Object obj = myLoader.loadClass("java.lang.Math").newInstance();        System.out.println(obj);    }

}
public final class Math {

public static void main(String[] args) {        System.out.println("hello world");    }}
public class MyMath {

public static void main(String[] args) {        System.out.println("hello world");    }}

上面的测试代码没用自定义java.lang.System类,因为测试代码用到了JDK自带的System类进行输出打印,会冲突,所以改用为自定义的java.lang.Math类。如果自定义的Math类能加载,那么自定义的System类同样能加载。

我们先直接运行下Math类,输出如下:

提示Math类没有main方法。首先大家要明白一个概念,当类首次主动使用时,必须进行类的加载,这部分工作是由类加载器来完成的。根据双亲委托原则,Math类首先由启动类加载器去尝试加载,很显然,它找到rt.jar中的java.lang.Math类并加载进内存(并不会加载我们自定义的Math类),然后执行main方法时,发现不存在该方法,所以报方法不存在错误。也就是说,默认情况下JVM不会加载我们自定义的Math类。

再直接运行MyMath类,输出如下:

注意红色部分的内容。由堆栈异常信息可知道,当应用程序类加载器类(AppClassLoader)尝试加载MyMath类时,ClassLoader.java的479行抛出了SecurityException

禁止使用包名:java.lang。

直接查看抽象类java.lang.ClassLoader的preDefineClass方法代码,摘抄如下:

可以看到如果加载的类全名称以“java.”开头时,将会抛出SecurityException,这也是为什么直接执行MyMath类会出现SecurityException。

照这样,我们自定义的类加载器必须继承自ClassLoader,其loadClass()方法里调用了父类的defineClass()方法,并最终调到preDefineClass()方法,因此我们自定义的类加载器也是不能加载以“java.”开头的java类的。我们继续运行下ClassLoaderTest类,输出如下:

红色部分清楚表明,也是在preDefineClass方法中抛出的SecurityException。

通过代码实例及源码分析可以看到,对于自定义的类加载器,强行用defineClass()方法去加载一个以"java."开头的类也是会抛出异常的。

总结

不能自己写以"java."开头的类,其要么不能加载进内存,要么即使你用自定义的类加载器去强行加载,也会收到一个SecurityException。

java 委托_java 能不能自己写一个类叫 java.lang.System/String 正确答案相关推荐

  1. vb6.0 定义一个公共类_纠正网上的错误:能不能自定义一个类叫java.lang.System/String?...

    前语:不要为了读文章而读文章,一定要带着问题来读文章,勤思考. 作者:一汪清水  来源:https://dwz.cn/i7Pf6VwZ 最近,学习了下java类加载相关的知识.然后看到网上有一道面试题 ...

  2. 面试干货 | Java 能否自定义一个类叫 java.lang.System?

    本文由读者 apdoer 投稿,apdoer 是一个极具钻研精神的 Java 猿,技术牛X头发茂盛! 博客地址:https://blog.csdn.net/m0_43452671 缘起:一个面试题 最 ...

  3. java 分子 分母_Java写一个类表示分数。分数由分子和分母组成。

    写一个类表示分数.分数由分子和分母组成. 写一个方法,实现分数相乘. 写一个方法,显示分数的值. 利用这个类:求分数 1/3 和 2/5 的乘积,并显示最终结果. public class zhouc ...

  4. Java (1)写一个类,名为Animal, 该类有两个私有属性,name(代表动物的名字),和legs(代表动物的腿的条数);要求为两个私有属性提供public的访问方法。并提供两个重载的构造方法,

    Java (1)写一个类,名为Animal, 该类有两个私有属性,name(代表动物的名字),和legs(代表动物的腿的条数);要求为两个私有属性提供public的访问方法.并提供两个重载的构造方法, ...

  5. 一个java应用程序必须且只有一个类含有什么方法_JAVA简答题!!!

    Java简答题 1.简述java语言的特点? 1.2 Java语言是目前使用最为广泛的网络编程语言之一(1分) 具有面向对象(1分) 与平台无关性(1分) 多线程性(1分) 动态性等特点(1分) 2. ...

  6. Java入门知识,写一个最简单java程序

    本文目录 一.Java语言的简介 二.写一个最简单Java程序 1.Notepad配置 2.最简单的Java程序 3.代码分析 4.编译与编译常见错误 5.执行java程序 一.Java语言的简介 0 ...

  7. java有且只有一个什么_1. 一个Java应用程序必须且只有一个类含有 main_____ 方法....

    1.一个Java应用程序必须且只有一个类含有main_____方法.2.构造方法是一种特殊方法,它的名字必须与它所在的类的名字完全相同,并不返回任何数据类型.3.子类自然地继承了其父类中不是... 1 ...

  8. 用java做一个简单记事本_用记事本写一个简单的java程序

    用记事本写一个简单的java程序 第一步: 安装好jdk,并设置好环境变量. 桌面-计算机(右键)-属性-高级系统设置-环境变量-path-在变量值后加上:和jdk安装路径加上(路径即为C:\Prog ...

  9. java中的复合数据类型是什么_【填空题】类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素。一个类的实现包括两部分:____和_____....

    [填空题]类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素.一个类的实现包括两部分:____和_____. 更多相关问题 [名词解释] 观叶树木 [单选] 开花时有浓郁香气的树种是 ...

最新文章

  1. WMI技术介绍和应用——查询快捷方式、映射磁盘和虚拟内存信息
  2. 星巴克、苹果、谷歌、亚马逊等巨头,为何同时做这件事?
  3. windows10安装mysql 8.0_手把手教你在Windows 10安装MySQL 8.0(详细图文)
  4. oracle authentication_services,理解SQLNET.AUTHENTICATION_SERVICES参数
  5. kwmpro音乐转换器序列号_ncm pro下载_ncm pro音乐转换器下载_3DM单机
  6. iOS - 视频开发
  7. 【420天】跃迁之路——程序员高效学习方法论探索系列(实验阶段177-2018.04.01)...
  8. Jenkins持久化集成使用
  9. ext2 无损升级 ext3、ext4
  10. Excel学习笔记:P22-时间格式、工龄与工时计算
  11. 百度地图 绘制运动轨迹_国产免费高配版“谷歌地球”,地图分析用这款软件秒杀谷歌地球...
  12. 魔百和盒子、中兴盒子、咪咕盒子、华为盒子、华为悦盒、烽火盒子、创维盒子、天邑盒子等运营商盒子固件合集分享
  13. Scala 插件安装
  14. 良心啊,做电商要是早点在这几个网站学习,也不至于被黑产坑啊
  15. excel转tsv格式
  16. 【MySQL综合练习1】
  17. 基于IPFS去中心化相册以太坊Dapp
  18. codevs1034
  19. Auto.js视频笔记
  20. 开源阅读书源_最好用的阅读软件之一

热门文章

  1. admin select 2 异步_解决Angularjs异步操作后台请求用$q.all排列先后顺序问题
  2. java将一个对象赋值给另一个对象_java一个对象赋值给另一个对象,支持平铺类和层级类间的互转...
  3. python科学计算三剑客_机器学习三剑客之Numpy
  4. l360废墨收集垫清零_知识分享003:EPSON L360打印机出现故障-废墨计数清零
  5. jquery 获取键值对中最大值_jquery属性的操作
  6. 汉字的ascii码值范围_ASCII代码
  7. 应用程序利用ADO对象访问数据库
  8. pycharm远程连接服务器(docker)调试+ssh连接多次报错
  9. Android实现边缘凹凸的View
  10. Python 中 异步协程 的 使用方法介绍