【问题标题】:Java: What happens when I create the same package as APIJava:当我创建与 API 相同的包时会发生什么
【发布时间】:2018-03-01 01:25:05
【问题描述】:

我很好奇如果我创建与项目具有的 API 中存在的完全相同的包会发生什么(例如,来自核心 Java API 的java.util)。编译器会给我一个错误还是什么?

如果编译器允许这样做,如果我创建一个具有原始 API 名称的类会发生什么?

另外,这是否意味着如果编译器不抱怨,包私有字段和方法可以访问?

【问题讨论】:

标签: java


【解决方案1】:

我很好奇,所以我做了自己的测试。我创建了java.util.concurrent 并使用此方法创建了一个Test 类:

public static Object getResult(CompletableFuture<?> cf) {
    return cf.result;
}

然后我在 main 方法中的其他地方调用它。没有编译错误。运行导致这个:

线程“主”java.lang.SecurityException 中的异常:禁止 包名:java.util.concurrent at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662) 在 java.lang.ClassLoader.defineClass(ClassLoader.java:761) 在 java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 在 java.net.URLClassLoader.defineClass(URLClassLoader.java:467) 在 java.net.URLClassLoader.access$100(URLClassLoader.java:73) 在 java.net.URLClassLoader$1.run(URLClassLoader.java:368) 在 java.net.URLClassLoader$1.run(URLClassLoader.java:362) 在 java.security.AccessController.doPrivileged(Native Method) 在 java.net.URLClassLoader.findClass(URLClassLoader.java:361) 在 java.lang.ClassLoader.loadClass(ClassLoader.java:424) 在 sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) 在 java.lang.ClassLoader.loadClass(ClassLoader.java:357) 在 sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

所以,它似乎与类加载器有关......或者我认为......直到我看了ClassLoader.preDefineClass()

// Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
// relies on the fact that spoofing is impossible if a class has a name
// of the form "java.*"
if ((name != null) && name.startsWith("java.")) {
    throw new SecurityException
        ("Prohibited package name: " +
         name.substring(0, name.lastIndexOf('.')));
}

所以,它阻止了我,因为 Java 想要保护所有以 java 开头的包!

我去尝试了别的东西。我用这个方法添加了javafx.scene 包和另一个Test 类:

public static boolean getBoundsChange(Node n) {
    return n.boundsChanged;
}

同样,没有编译错误,但它在运行时抛出这个:

原因:java.lang.IllegalAccessError: 试图访问字段 javafx.scene.Node.boundsChanged 来自类 javafx.scene.TestNode,位于 javafx.scene.TestNode.getBoundsChange(TestNode.java:7) 在 testFX.start(testFX.java:29) 在 com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863) 在 com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326) 在 com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295) 在 java.security.AccessController.doPrivileged(Native Method) 在 com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294) 在 com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) 在 com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 在 com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191) ... 1 更多

似乎 Java 确实可以防止这些,但仅在运行时这样做。

我在javafx.scene 中创建了另一个接口,并让它扩展Consumer&lt;T&gt;。我可以像原来的 Consumer&lt;T&gt; 一样使用它,所以没有什么能阻止我将这个包用于我的新课程。

【讨论】:

  • 它可能依赖于虚拟机,我在我的 Android 项目中创建了 java.util.concurrent 包,它运行良好。
  • @urgentx 你的意思是你可以调用包私有方法/字段并做它应该做的任何事情,而不会出现异常或错误?
  • 不,如果我重新声明一个已经存在的 util.concurrent 类,它不会导入它,但仍会编译。如果我在 java.util.concurrent 中创建一个类 Test 并且我可以在其他地方使用它而不会出现问题。
【解决方案2】:

如果包名被禁止,您将被类加载器阻止,例如java,或者如果您试图闯入密封在分发它的 JAR 文件中的包。

【讨论】:

  • IllegalAccessError 是否与类加载器有关?
  • @Jai 不,ClassLoader 那时没有运行。我不知道你得到的IllegalAccessException 的机制。
【解决方案3】:

Java 包由文件系统上的文件夹支持,因此您不能创建包的副本,因为它只是同一个文件夹。

没有什么能阻止您在自己的项目中创建 java.util 包并使用它。这将是一个唯一的包名,与 Java API 无关。

【讨论】:

  • 他没有说任何关于课程的事情。我知道 OP 对查看包装可见的东西更感兴趣。
  • Java 包也可以在 jar 文件中,因此您很可能会因为具有相同包和类的不同 jar 文件而产生冲突。
  • 这正是他所做的,而且失败了。您不能在 java.* 包中创建类。 JVM 不会加载它们。
  • @EJP 它似乎依赖于 VM/构建工具,我在 Android 项目中这样做没有问题。
猜你喜欢
  • 1970-01-01
  • 2015-09-16
  • 2015-02-04
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多