为什么我能够重新创建java.lang包和类?

2022-09-01 04:36:43

我只是在玩包结构。令我惊讶的是,我可以通过创建具有该名称的包和类名来绕过默认类。

例如:

我创建了一个名为 和 类 是 的包。当我导入时,它不是JDK版本的.是我的。它只是显示了每个对象java都有的方法。java.langBooleanjava.lang.BooleanBooleanObjects

为什么会这样?为什么允许我创建软件包?并且程序运行良好。java.lang

enter image description here

另一个障碍是,如果我创建一个带有名称并尝试运行该程序,则异常ClassObject

java.lang.SecurityException: Prohibited package name: java.lang
    at java.lang.ClassLoader.preDefineClass(Unknown Source)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)

为什么会有这种行为?这是一个错误还是正常行为?


答案 1

对类的限制是运行时限制,而不是编译时限制。java.lang

JVM 实际上专门提供了一种用于重写 中的类的机制。您可以使用 -Xbootclasspath 命令行标志来执行此操作:java.lang

-Xbootclasspath:bootclasspath
指定以分号分隔的目录、JAR 文件和 ZIP 归档文件列表以搜索引导类文件。它们用于代替 Java 平台 JDK 中包含的引导类文件。

不应部署使用此选项来重写 rt.jar 中的类的应用程序,因为这样做会违反 Java 运行时环境二进制代码许可证。

-Xbootclasspath/a:path
指定要追加到缺省引导类路径的目录、JAR 文件和 ZIP 归档文件的分号分隔路径。

-Xbootclasspath/p:path
指定要添加到缺省引导类路径前面的以分号分隔的目录、JAR 文件和 ZIP 归档文件的路径。

不要部署使用此选项在 rt.jar 中重写类的应用程序,因为这违反了 Java 运行时环境二进制代码许可证。

但是,正如我已经用粗体标记强调的那样,这样做违反了Java SE和JavaFX Technologies的Oracle二进制代码许可协议

D.JAVA技术限制。您不得创建、修改或更改以任何方式标识为“java”、“javax”、“javafx”、“sun”、“oracle”或 Oracle 在任何命名约定中指定的类似约定的类、接口或子包的行为,也不得授权您的被许可方创建、修改或更改其行为。您不得重新分发附表 1 中列出的软件。

除了上述之外,您还可以将所需的任何类添加到所需的任何包中;在JLS §13.3中对此进行了具体讨论:

13.3. 软件包的演变

可以将新的顶级类或接口类型添加到包中,而不会破坏与预先存在的二进制文件的兼容性,前提是新类型不重用以前为不相关类型指定的名称。

如果新类型重用以前为不相关类型指定的名称,则可能会导致冲突,因为同一类装入器无法装入这两种类型的二进制文件。

顶级类和接口类型中的更改,如果不是公共类型的超类或超接口,则分别是公共类型的,仅影响声明它们的包中的类型。此类类型可能会被删除或以其他方式更改,即使此处另有描述不兼容,前提是该包的受影响二进制文件一起更新。


答案 2

相关问题的回答:SecurityException

SecurityManger 会抛出这个 RuntimeException,而你的类加载器调用方法并遇到指定的类(你的“自定义类”)名称中包含“java.*”。defineClass

这是因为您在“java.*”包中定义了您的类,并且根据ClassLoader的文档,这是不允许的。

定义类( )

..

指定的名称不能以“java.”开头,因为“java.* 包中的所有类只能由引导类装入器定义。如果 name 不为 null,则它必须等于字节数组 “b” 指定的类的二进制名称,否则将引发 NoClassDefFoundError。

抛出:。。

SecurityException - 如果尝试将此类添加到包含由与此类不同的证书集签名的类的包中,或者如果 name 以“java.”开头。

对于测试,请尝试创建包并定义一个自定义类(名称无关紧要;如 ..)。在这种情况下,您还将获得相同的SecurityException。java.testObject

package java.test;

public class Test {

    public static void main(String[] args) {

        System.out.println("This is Test");
    }
}