无法从不同 jar 中的同一包访问超类的受保护成员

2022-09-04 21:25:45

我遇到了一个奇怪的问题,我无法弄清楚在尝试插件化我的程序时弹出的问题。另一个问题是我无法创建一个简单的测试用例,因为每次我尝试它都能正常工作。我一定错过了一些复杂情况。但我会尽量清楚地描述这种情况,以防万一它听起来很熟悉。

我有一个名为 Seed 的基类,它是主应用程序的一部分,由系统类加载器加载。我有一个插件,其中包含一个类Road,它是Seed的子类。它是在运行时从单独的 jar 文件加载的。类 Road 引用了 Seed.garden 字段,该字段定义为:

受保护的最终花园花园;

请注意,我没有收到编译错误。当插件jar包含在系统类路径中时,我也不会收到运行时错误。只有当我的主应用程序使用新的类加载器(以系统类加载器作为其父级)加载插件时,我才会收到错误。错误是:

java.lang.IllegalAccessError: 尝试访问字段包。Seed.garden from class package.路$4

这肯定与子类由与超类不同的类加载器加载的事实有关,但我找不到任何官方理由为什么这不应该起作用。另外,就像我说的,当我尝试用一个简单的测试用例(包括单独的jar,用不同的类加载器加载子类等)重现问题时,我不会得到错误。

我似乎也不太可能违反访问规则,因为当类由同一类加载器加载时,它可以工作,并且我不会收到编译错误。

我没有想法了!有没有人认识到这个问题,或者为我提供了一些方向的指针?帮助!


答案 1

好吧,所以在axtavt和其他受访者的帮助下,我弄清楚了问题所在。其他答案有所帮助,但他们并没有完全正确,这就是为什么我要回答自己的问题。问题原来是“运行时包”的概念,在Java虚拟机规范中定义如下:

5.3 创建和加载

...在运行时,类或接口不是由其名称单独决定的,而是由一对确定的:其完全限定的名称和定义类装入器。每个这样的类或接口都属于一个运行时包。类或接口的运行时包由类或接口的包名称和定义类装入器确定。...

5.4.4 访问控制

...当且仅当满足以下任何条件为真时,类或接口 D 可以访问字段或方法 R:...

  • R 受到保护,并在类 C 中声明,D 是 C 或 C 本身的子类。
  • R 要么是受保护的,要么是包私有的(即既不是公共的,也不是受保护的也不是私有的),并且由与 D 相同的运行时包中的类声明。

第一个子句解释了为什么允许 Road 访问 Seed.garden,因为 Road 是 Seed 的子类,第二个子句解释了为什么不允许 Road$4 访问它,尽管它与 Road 位于同一个包中,因为它不在同一个运行时包中,由不同的类加载器加载。该限制实际上不是 Java 语言限制,而是 Java VM 限制。

因此,我的情况的结论是,由于Java VM的合法限制,异常发生了,我将不得不解决这个问题,可能是通过公开字段,在这种情况下这不是问题,因为它们是最终的,而不是秘密的,或者也许通过Road将Seed.garden导出到Road$4, 它确实具有访问权限。

谢谢大家的建议和回答!


答案 2

听起来你遇到了类身份危机,让两个不同的类装入器在类层次结构或类似的类中加载相同的类。阅读一些关于java类装入器的信息。这里有一个很好的介绍,对于“阶级身份危机”见图2:http://www.ibm.com/developerworks/java/library/j-dyn0429/


推荐