能否自定义类名为 java.lang.System / java.lang.String?¶
结论¶
不能。即使你写了一个 package java.lang; public class System {},也无法被正常加载,更无法覆盖 JDK 自带的类。根本原因是 类加载的双亲委派机制。
为什么不行¶
1. 启动类加载器(Bootstrap ClassLoader)优先加载¶
java.lang.System、java.lang.String 这些核心类由 Bootstrap ClassLoader(C++ 实现,加载 $JAVA_HOME/jre/lib/rt.jar)加载。
当你的代码或自定义类加载器请求加载 java.lang.System 时,先委派给父加载器,最终委派到 Bootstrap,它已经加载了 JDK 自带的版本,直接返回,根本不会加载你写的那个类。
2. 即使绕过重载,也会被安全管理器拦截¶
JDK 6 以后,ClassLoader#defineClass 对 java. 开头的包名做了硬检查:
// ClassLoader#preDefineClass
if (name != null && name.startsWith("java.")) {
throw new SecurityException(
"Prohibited package name: " + name);
}
任何 java.* 包下的类,只允许 Bootstrap ClassLoader 定义。
验证¶
package java.lang;
public class System {
public static void main(String[] args) {
System.out.println("hello"); // 这里的 System 指哪个?
}
}
直接运行会抛:
如果真的想"修改"核心类¶
只能用 BootClassPath 扩展 或 -Xbootclasspath/p: 提前注入自己的版本(已废弃/高风险),或使用 Java Agent(Instrumentation)在运行时重定义类。但这些都是hack 手段,生产环境绝不可取。
安全含义
双亲委派 + java. 包名检查是 JVM 的安全基石,防止恶意代码冒充核心类窃取或篡改行为。面试中答出这一点会显著加分。