为啥我能够重新创建 java.lang 包和类?
Posted
技术标签:
【中文标题】为啥我能够重新创建 java.lang 包和类?【英文标题】:Why I am able to re-create java.lang package and classes?为什么我能够重新创建 java.lang 包和类? 【发布时间】:2015-11-14 00:12:00 【问题描述】:我只是在玩包结构。令我惊讶的是,我可以通过使用该名称创建我的包和类名来绕过默认类。
例如:
我创建了一个名为java.lang
的包,类为Boolean
。当我导入java.lang.Boolean
时,它不是JDK 的Boolean
版本。这是我的。它只是显示了每个java对象都有的Objects
的方法。
为什么会这样?为什么允许我创建包java.lang
?并且程序运行良好。
另一个问题是,如果我创建一个名为 Object
的 Class
并尝试运行该程序,则会出现异常
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)
为什么会出现这种行为?这是错误还是正常行为?
【问题讨论】:
使用自定义Boolean
时程序的输出是什么?你能发布 full 堆栈跟踪吗?
【参考方案1】:
回答SecurityException
相关问题:
SecurityManger 在您的类加载器调用 defineClass
方法并遇到指定的类(您的“自定义类”)名称中包含“java.*”时抛出此 RuntimeException。
这是因为您在“java.*”包中定义了您的类,并且根据ClassLoader's documentation 这是不允许的。
定义类()
..
指定名称不能以“java.”开头,因为“java.*”包中的所有类只能由引导类加载器定义。如果名称不为空,则必须等于由字节数组“b”指定的类,否则会抛出 NoClassDefFoundError。
抛出: ..
SecurityException - 如果尝试将此类添加到包含由与此类不同的证书集签名的类的包中,或者名称以“java”开头。
为了您的测试,请尝试创建java.test
包并定义一个自定义类(名称无关紧要;例如Object
..)。在这种情况下,您也会得到相同的 SecurityException。
package java.test;
public class Test
public static void main(String[] args)
System.out.println("This is Test");
【讨论】:
如果您创建了一个已经存在的类,如java.lang.System
或java.lang.Object
,则不应发生这种情况。仅当您尝试将新类引入 java.* 包时才会引发异常。至少当我尝试时会发生这种情况。【参考方案2】:
java.lang
类的限制是运行时限制,而不是编译时限制。
JVM 实际上专门提供了一种机制来覆盖java.lang
中的类。您可以使用-Xbootclasspath
command line flag:
-Xbootclasspath:bootclasspath
指定以分号分隔的目录、JAR 文件和 ZIP 归档列表以搜索引导类文件。这些用于代替 Java 平台 JDK 中包含的引导类文件。不应部署使用此选项来覆盖 rt.jar 中的类的应用程序,因为这样做会违反 Java 运行时环境二进制代码许可。
-Xbootclasspath/a:path
指定要附加到默认引导类路径的目录、JAR 文件和 ZIP 档案的分号分隔路径。
-Xbootclasspath/p:path
指定以分号分隔的目录、JAR 文件和 ZIP 存档路径,以添加到默认引导类路径的前面。不要部署使用此选项覆盖 rt.jar 中的类的应用程序,因为这违反了 Java 运行时环境二进制代码许可。
但是,正如我已经用粗体标记强调的那样,这样做违反了Oracle Binary Code License Agreement for Java SE and JavaFX Technologies:
D. JAVA 技术限制。 您不得创建、修改或更改其行为,或授权您的被许可人创建、修改或更改以任何方式标识为“java”、“javax”的类、接口或子包的行为"、"javafx"、"sun"、"oracle" 或 Oracle 在任何命名约定指定中指定的类似约定。您不得再分发附表 1 中列出的软件。
除了上述之外,您还可以将您想要的任何类添加到您想要的任何包中;在JLS §13.3:
13.3。包的演变
新的***类或接口类型可以添加到包中而不会破坏与预先存在的二进制文件的兼容性,前提是新类型不会重用先前赋予无关类型的名称。
如果一个新类型重用了之前为不相关类型指定的名称,则可能会导致冲突,因为这两种类型的二进制文件无法由同一个类加载器加载。
***类和非公共接口类型的更改分别不是公共类型的超类或超接口,仅影响声明它们的包中的类型。此类类型可能会被删除或以其他方式更改,即使此处以其他方式描述了不兼容性,只要该软件包的受影响二进制文件一起更新。
【讨论】:
我完全同意你所说的,但这并不能解释为什么 OP 只面对SecurityException
Object
而不是 Boolean
。
@manouti 我无法重现这种行为。在我的项目中它不起作用。 Java 8 预言机。
完全正确(我也无法复制)。【参考方案3】:
您将 java.lang.Boolean 作为布尔类而不是 Object 的问题很容易解释。 Object 类是您可以找到、使用甚至创建的所有其他类的根。这意味着,如果您能够覆盖它,那么没有一个类、方法或您想使用的任何东西都可以工作,因为它们中的每一个都依赖于该根类。 对于布尔类,它不是布尔类型,而是布尔类型的类。由于没有任何东西依赖于它,因此可以覆盖它。 更好地理解这个问题的方法是看这个链接: [http://docs.oracle.com/javase/7/docs/api/overview-tree.html] 你会注意到每一种包,包含每一种java类,都依赖于对象类。
所以您遇到的安全异常就像您的程序的“救生员”。 如果我对您的问题有误,其他人可能会找到更合适的答案。 :)
【讨论】:
【参考方案4】:这不是错误。
行为是因为:
当 Java 虚拟机 (JVM) 尝试加载我们的类时,它会将其包名识别为无效,因此会引发 SecurityException。 SecurityException 表示发生了安全违规,因此无法执行应用程序。 公共类 SecurityException 扩展 RuntimeException 由安全经理抛出以表明安全违规。
请使用不同的包名,它不仅适用于 java 的语言包。它涵盖了所有包,不授予在构建类和 java 包中覆盖的权限。
通过改变这个我们可以创建或覆盖相同的包和类:
a/j2ee.core.utilities/src/org/netbeans/modules/j2ee/core/api/support/java/JavaIdentifiers.java b/j2ee.core.utilities/src/org/netbeans/modules/j2ee/core/api/support/java/JavaIdentifiers.java
**if (packageName.startsWith(".") || packageName.endsWith(".")) // NOI18N
return false;
if(packageName.equals("java") || packageName.startsWith("java.")) //NOI18N
return false;
**
String[] tokens = packageName.split("\\."); //NOI18N
if (tokens.length == 0)
return Utilities.isJavaIdentifier(packageName);
a/j2ee.core.utilities/test/unit/src/org/netbeans/modules/j2ee/core/api/support/java/JavaIdentifiersTest.java b/j2ee.core.utilities/test/unit/src/org/netbeans/modules/j2ee/core/api/support/java/JavaIdentifiersTest.java
assertFalse(JavaIdentifiers.isValidPackageName(" "));
assertFalse(JavaIdentifiers.isValidPackageName("public"));
assertFalse(JavaIdentifiers.isValidPackageName("int"));
assertFalse(JavaIdentifiers.isValidPackageName("java"));
assertFalse(JavaIdentifiers.isValidPackageName("java.something"));
【讨论】:
以上是关于为啥我能够重新创建 java.lang 包和类?的主要内容,如果未能解决你的问题,请参考以下文章