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

时间:2012-05-05 20:20:02

标签: java inheritance jar

我遇到了一个奇怪的问题,我无法弄清楚在尝试插入程序时弹出的问题。另一个问题是我无法创建一个简单的测试用例,因为每次我尝试它都有效。必须有一些我不知道的并发症。但我会尽可能清楚地描述情况,以防任何人听起来很熟悉。

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

受保护的最终花园;

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

java.lang.IllegalAccessError:尝试从类package.Road $ 4

访问字段package.Seed.garden

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

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

我没有想法!有没有人认识到这个问题,或者有一些指示我的方向可以看?救命啊!

4 个答案:

答案 0 :(得分:8)

好的,所以在axtavt和其他受访者的帮助下,我发现了问题所在。其他答案有所帮助,但他们并没有完全正确,这就是为什么我回答我自己的问题。问题结果是“运行时包”的概念,在Java Virtual Machine specification中定义如下:

5.3创建和加载

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

5.4.4访问控制

... 当且仅当满足以下任一条件时,才能访问类或接口D的字段或方法R:...

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

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

所以我的情况的结论是由于Java VM的合法限制而发生异常,我将不得不解决它,可能是通过将字段公开,这不是一个问题因为它们是最终的,而不是秘密的,或者可能通过道路将Seed.garden出口到Road $ 4,它可以访问。

感谢大家的建议和解答!

答案 1 :(得分:4)

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

答案 2 :(得分:1)

  

我应该补充一点,道路4美元是一个匿名的内部道路......

其他人认为这也是1998年的一个错误:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4116802

  

内部类对其他类的成员没有更多的访问权限   顶级类,但声明的成员除外   封闭的,封闭的或兄弟的类。这是一种常见的误解   内部类具有对受继承成员的无限制访问权限   它的封闭课程。事实并非如此。

我可能会更多地研究这个事实,因为这最初是针对Java 1.2报告的,但我似乎记得从我的阅读中看到今天也是如此。

修改

我确认这是真的:

http://docs.oracle.com/javase/tutorial/java/javaOO/summarynested.html

匿名内部类的范围只是定义它的范围。因此,即使外部类也可以访问继承的成员。

答案 3 :(得分:0)

这是权限错误,因此它取决于您用于运行运行时的框架。 只是为了澄清这确实是这样,让父成员公开,然后尝试运行。如果一切正常,那么恢复你的代码,根据你使用的运行时,我们需要配置正确的安全访问。