在JDK中内置了很多类,我们能否自己写一个类去“覆盖”这些内置的类呢?
(正常手段下)不能!
Talk is cheap. Show me the code,以下我们尝试自己在src中编写一个java.lang.String,用于“覆盖”JDK中的String。
自己在src中编写的java.lang.String源码如下所示(包名和类名都与jdk中的String一致)
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("hello String...");
}
}
但在运行时,会报以下异常:
错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
异常提示:在类 java.lang.String 中找不到 main 方法。
why?
why?
why?
因为我们的JVM设计者早就想到了这种情况,担心哪个核心类不小心被你碰巧给重名了,造成运行时的诡异错误。为此(当然还有其他原因),JVM内部存在着一个名为“双亲委派”的机制。
双亲委派是JVM在加载类时所遵循的一个机制。简单的说,就是当一个加载器要加载类的时候,自己先不加载,而是逐层向上交由双亲(类似于父亲)去加载;当双亲中的某一个加载器 加载成功后,再向下返回成功。如果所有的双亲和自己都无法加载,则报异常。一句话总结:加载器的层次越高,执行的优先级就越高。
双亲委托中各个加载器的层级关系如下所示。
其中,各个加载类的分类及作用如下所示:
- JVM自带的加载器
- 根加载器,Bootstrap :
加载 jrelibrt.jar (包含了Object、String、Math等平时编写代码时 大部分的jdk API);也可以通过JVM参数-Xbootclasspath指定加载某一个jar - 扩展类加载器,Extension:加载jre中libext*.jar中的类 ;也可以通过JVM参数-Djava.ext.dirs指定加载某一个jar
- AppClassLoader/SystemClassLoader,系统加载器(也称为应用加载器):加载classpath(本题中的src);也可以通过JVM参数-Djava.class.path指定加载某一个类/jar
- 用户自定义的加载器
- 都是抽象类java.lang.ClassLoader的子类,用于自定义加载
本题,程序在执行时识别的是src中的java.lang.String,src就是classpath,因此会调用系统加载器。但根据双亲委派机制,系统加载器会逐层委派双亲来加载此类,在委派的时候,最上层的加载器是根加载器,即根加载器优先级最高。而根加载器能够在jrelibrt.jar包中找到一个重名的java.lang.String(即jdk自带的String),因此根据双亲委派最终会由最顶层的根加载器来执行jdk自带的java.lang.String。显然,jdk中的String并没有main()方法,因此报错找不到main()!!!
--- 完 ---