昨天我在Tomcat 8上部署我的Java 8 webapp后遇到了一个有趣的问题.我不想如何解决这个问题,而是更了解为什么会发生这种情况.但是,让我们从头开始.
我有两个类定义如下:
Foo.java
package package1;
abstract class Foo {
public String getFoo() {
return "foo";
}
}
Run Code Online (Sandbox Code Playgroud)
Bar.java
package package1;
public class Bar extends Foo {
public String getBar() {
return "bar";
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,它们在同一个包中,并最终在同一个jar中,我们称之为commons.jar.这个jar是我的webapp的依赖(即在我的webapp的pom.xml中被定义为依赖).
在我的webapp中,有一段代码可以:
package package2;
public class Something {
...
Bar[] sortedBars = bars.stream()
.sorted(Comparator.comparing(Bar::getBar)
.thenComparing(Bar::getFoo))
.toArray(Bar[]::new);
...
}
Run Code Online (Sandbox Code Playgroud)
当它被执行时,我得到:
java.lang.IllegalAccessError: tried to access class package1.Foo from class package2.Something
Run Code Online (Sandbox Code Playgroud)
玩耍和试验我能够通过三种方式避免错误:
将Foo类更改为public而不是package-private;
将Something类的包更改为"package1"(即字面上与Foo和Bar类相同,但物理上不同的是webapp中定义的Something类);
在执行违规代码之前强制加载Foo:
try {
Class<?> fooClass = Class.forName("package1.Foo");
} …Run Code Online (Sandbox Code Playgroud)最近,当我java.lang.IllegalAccessError尝试从内部类访问由不同类加载器加载的外部父类中声明的受保护字段时,我遇到了运行时错误的问题。简要地:
Parent有受保护的字段p。Outer扩展Parent。Inner是在 class 中定义的内部类Outer。Inner类里面有一个代码:Outer.this.p.通常它的编译和运行正常,直到Parent和Outer类是由不同的类加载器加载。在这种情况下,我们java.lang.IllegalAccessError在尝试Outer.this.p从Inner. 我发现了一个旧的错误报告(这似乎是一个功能)描述了这种行为:
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6258289
但分辨率对我来说听起来自相矛盾:
关键是在失败的情况下,内部类不在同一个包中(并且不是 ConcreteCommand/AbstractCommand 的子类)。这只是违反了受保护类的 Java 规范。
听起来是对的。但是,如果我们在不同的包中声明Parent和Outer类,但使用单个类加载器加载(只需创建没有任何 jar 加载的示例控制台应用程序),我们就不会收到任何错误。因此,从技术上讲,它违反了受保护类的 Java 规范,但由于我们使用了内部类,因此它可以工作。
因此,对于“不同的包”的两种情况,我们有不同的行为。
有人可以清楚地解释内部类如何访问父类的字段以及为什么它在两种情况下的工作方式不同吗?
在尝试使用默认访问器覆盖方法时,我遇到了一种奇怪的行为(例如:)void run().根据Java规范,如果类属于同一个包,则类可以使用或覆盖基类的默认成员.当所有类从同一个类加载器加载时,一切正常.但是如果我尝试从单独的类加载器加载子类,则多态性不起作用.
这是样本:
App.java:
import java.net.*;
import java.lang.reflect.Method;
public class App {
public static class Base {
void run() {
System.out.println("error");
}
}
public static class Inside extends Base {
@Override
void run() {
System.out.println("ok. inside");
}
}
public static void main(String[] args) throws Exception {
{
Base p = (Base) Class.forName(Inside.class.getName()).newInstance();
System.out.println(p.getClass());
p.run();
} {
// path to Outside.class
URL[] url = { new URL("file:/home/mart/workspace6/test2/bin/") };
URLClassLoader ucl = URLClassLoader.newInstance(url);
final Base p …Run Code Online (Sandbox Code Playgroud) java ×3
classloader ×2
inheritance ×1
java-8 ×1
maven ×1
maven-3 ×1
polymorphism ×1
protected ×1