原文: http://www.wfuyu.com/php/22254.html

未做测试 !

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

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

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

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

首先,摘抄网上毛病答案的详细解释

“为了不让我们写System类,类加载采取拜托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。
但是,我们可以自己定义1个类加载器来到达这个目的,为了不双亲拜托机制,这个类加载器也必须是特殊的。由于系统自带的3个类加载器都加载特定目录下的类,如果我们自己的类加载器放在1个特殊的目录,那末系统的加载器就没法加载,也就是终究还是由我们自己的加载器加载。”

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

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

类加载器是有层次的

1般是: 自定义类加载器  >> 利用程序类加载器  >> 扩大类加载器  >> 启动类加载器

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

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

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

代码验证

测试类结构及内容以下:

public class MyClassLoader extends ClassLoader{ public MyClassLoader() { super(null); } @Override public 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类,输出以下:

java.lang.NoSuchMethodError: main
Exception in thread "main"

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

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

java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:479)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:625)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
Exception in thread "main"

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

制止使用包名:java.lang。

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

private ProtectionDomain preDefineClass(String name, ProtectionDomain protectionDomain) { if (!checkName(name)) throw new NoClassDefFoundError("IllegalName: " + name); if ((name != null) && name.startsWith("java.")) { throw new SecurityException("Prohibited package name: " + name.substring(0, name.lastIndexOf('.'))); } if (protectionDomain == null) { protectionDomain = getDefaultDomain(); } if (name != null) checkCerts(name, protectionDomain.getCodeSource()); return protectionDomain; }

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

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

/java/lang/Math.class
sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@a981ca
java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:479)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:625)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.lang.ClassLoader.defineClass(ClassLoader.java:465)
at com.tq.MyClassLoader.loadClass(MyClassLoader.java:28)
at com.tq.ClassLoaderTest.main(ClassLoaderTest.java:8)
Exception in thread "main" java.lang.ClassNotFoundException
at com.tq.MyClassLoader.loadClass(MyClassLoader.java:31)
at com.tq.ClassLoaderTest.main(ClassLoaderTest.java:8)

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

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

总结

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

码字不容易,源头来自http://blog.csdn.net/tang9140。

那些抓取网上内容,放在自家网站上的请自觉。百度搜索你为啥老把原创网页排后面呢,请反思,不要只想着赚钱。

java能不能自己写一个类叫java.lang.System/String正确答案的更多相关文章

  1. 能否自己也写一个类叫做java.lang.String?

    这次的随笔很逗吧~没错,我们的确也可以自己在创建一个包java.lang,然后在 相应的包下面创建一个对应的类String,但是在每次jre运行的时候,我们都回去加载原来默认的java.lang.St ...

  2. Java基础-接口.编写2个接口:InterfaceA和InterfaceB;在接口InterfaceA中有个方法void printCapitalLetter();在接口InterfaceB中有个方法void printLowercaseLetter();然 后写一个类Print实现接口InterfaceA和InterfaceB,要求 方法 实现输出大写英文字母表的功能,printLowerca

    #34.编写2个接口:InterfaceA和InterfaceB:在接口InterfaceA中有个方法void printCapitalLetter():在接口InterfaceB中有个方法void ...

  3. Java中直接输出一个类的对象

    例如 package com.atguigu.java.fanshe; public class Person { String name; private int age; public Strin ...

  4. Java中如何查看一个类依赖的包

    Java中如何查看一个类依赖的包 如图, 我如何知道JSONArray是依赖的哪一个包呢,这里有两个json-lib包?   测试语句:   public static void main(Strin ...

  5. java面试题0004-在一个类上是否可以用abstract和final同时加以修饰?

    我们先用提干两个修饰词中的任意一个创建一个类 package components.javaTest.day4_20200910; /** * Question004: * java面试题0004-在 ...

  6. Java项目中每一个类都可以有一个main方法

    Java项目中每一个类都可以有一个main方法,但只有一个main方法会被执行,其他main方法可以对类进行单元测试. public class StaticTest { public static ...

  7. 能不能自己写个类,也叫java.lang.String?

    可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String.由于在tomcat的web应用程序中,都是由webapp自 ...

  8. java通过反射取得一个类的完整结构

    首先我们在person包中新建一个Person.java: package person; import sex.Sex; public class Person{ private String na ...

  9. Java编程题: 写一个Singleton出来

    Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 一般Singleton模式通常有几种种形式: 第一种形式:  定义一个类,它的构造函数为private的, ...

随机推荐

  1. 使用Ajax实现的批量删除操作(C#)

    今天做了一个简单的批量删除操作,虽然简单,但是很多问题出现,终究还是技术不够熟练. 现在在这里跟大家分享一下.仅供学习... 1.在前台获取用户点击的信息id,把这里id封装到一个数组里面:(rows ...

  2. DSET收集ESXi硬件日志

    1.Open DSET CLI with administrator mode C:\Windows\system32>dellsysteminfo -s 10.125.1.xxx -u roo ...

  3. 报错:org.hibernate.AssertionFailure: null id in com.tt.hibernate.entities.News entry (don't flush the Session after an exception occurs)

    在使用hibernate创建数据库的表格时,出现了如下报错: 十二月 28, 2016 10:17:02 上午 org.hibernate.tool.hbm2ddl.SchemaExport perf ...

  4. Hadoop总结篇之五---模块间是怎么驱动执行的

    在MRv1中,各个模块间驱动运行的方式是函数调用的方式.这是同步的过程,上一模块调用下一模块函数后,等待其执行.效率不高. 在MRv2中做了改进,yarn基于事件驱动的并发模型.在详细介绍前,先看下图 ...

  5. 深入理解Session与Cookie

    Session与cookie的作用都是为了保持访问用户与后端服务器的交互状态. cookie通过把所有要保存的数据通过HTTP协议的头部从客户端传递到服务端,又从服务端再传回到客户端,所有的数据都存储 ...

  6. Win+R命令大全

    explorer---------------------打开资源管理器logoff------------------------注销命令lusrmgr.msc----------------本机用 ...

  7. python 类变量和实例变量

    super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类.  实例的属性存储在实例的__dict__中,类属性和方法存储在类的__dict__中.查找属性时,先检 ...

  8. android 代码设置progressBar 颜色

    void test() { LinearLayout linearLayout = new LinearLayout(this); ProgressBar progressBar = new Prog ...

  9. Json序列化,date类型转换后前端显示错误的解决方案

    1.前台使用Jquery解决 (1)如果我们前台使用Jquery来解决这个问题的话,那么我们首先想到的是我们如何解析这个过程呢,当然我们就想到了自己写一个JavaScript脚本来解析这个过程,当然这 ...

  10. dos笔记

    MS DOS 命令大全 一.基础命令 1 dir 无参数:查看当前所在目录的文件和文件夹. /s:查看当前目录已经其所有子目录的文件和文件夹. /a:查看包括隐含文件的所有文件. /ah:只显示出隐含 ...