【问题标题】:Java: accessing protected fields from inner classJava:从内部类访问受保护的字段
【发布时间】:2020-11-02 13:40:19
【问题描述】:

最近,当我尝试从内部类访问由不同类加载器加载的外部父类中声明的受保护字段时,我遇到了运行时错误java.lang.IllegalAccessError 的问题。简要说明:

  1. Parent 具有受保护字段p
  2. Outer 类扩展 Parent
  3. Inner 类是在Outer 类中定义的内部类。
  4. Inner 类中有一个代码:Outer.this.p
  5. 所有类都在同一个包中声明。

通常在ParentOuter 类被不同的类加载器加载之前,它会被编译并运行良好。在这种情况下,当我们尝试从Inner 访问Outer.this.p 时,我们会得到java.lang.IllegalAccessError。 我发现了一个描述这种行为的旧错误报告(这似乎是一个特性):

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6258289

但分辨率听起来与我矛盾:

关键是在失败的情况下内部类不在同一个 包(不是子类)ConcreteCommand/AbstractCommand。 这简直违反了受保护的 Java 规范 类。

听起来很正确。但是,如果我们在不同的包中声明 ParentOuter 类但使用单个类加载器加载(只需创建没有任何 jar 加载的示例控制台应用程序),我们不会收到任何错误。所以从技术上讲,这违反了受保护类的 Java 规范,但由于我们使用了内部类,所以它可以工作。

因此,对于两种“不同的包”,我们有不同的行为。

  1. 在不同的包中声明,由单个类加载器加载 - 好的。
  2. 在单个包中声明,由不同的类加载器加载 - 不正常。

有人可以清楚地解释内部类如何访问父级的字段以及为什么它在两种情况下的工作方式不同吗?

【问题讨论】:

  • 如何重现类被不同类加载器加载的场景?

标签: java classloader inner-classes protected access-modifiers


【解决方案1】:
  • 似乎是同一个类加载器
  • 我答对了吗?
  • 您是否有任何单元测试用例可以重现您的问题?

父类

package p1;

public class Parent {
    
    protected String p = "Value from Parent";
    
    public void test() {
        System.out.println(p);
    }

}

外部类

package p1;

public class Outer extends Parent {

    class Inner {
        public void test() {
            Outer.this.p = "Value set from Inner";
            System.out.println(Outer.this.p);
        }
    }

    public void test() {
        new Inner().test();
    }
}

主类

package p1;

public class Main {

    public static void main(String[] args) {
        Parent p = new Parent();
        p.test();
        p = new Outer();
        p.test();
    }
}

输出

Value from Parent
Value set from Inner

【讨论】:

    【解决方案2】:

    在不同的包中声明,由单个类加载器加载 - OK

    'protected' 访问考虑到类之间的父子关系,并允许子类访问父类的“受保护”成员,即使它们位于不同的包中。所以,我认为这符合预期。

    在单个包中声明,由不同的类加载器加载 - 不行

    这与运行时包有关。检查this。 现在我们知道 Parent 与 Outer 和 Inner 位于不同的运行时包中,因为它是通过两个不同的类加载器加载的。同时,我们还必须记住,Outer 是 Parent 的“孩子”,而 Inner 不是。 Inner 与 Parent 没有“Is-a”关系。

    把它们放在一起: 由于 Parent 位于不同的运行时包中,因此 Inner 无法访问 Parent 的“受保护”成员,因为 Inner 不是 Parent 的孩子。

    【讨论】:

    • 这对我来说非常清楚。这里的问题是:当它们被单个类加载器加载时,尽管在不同的包中声明了内部类,但它们是否可以访问外部类的父类的受保护字段? Inner 仍然没有与 Parent 处于“Is-a”关系。
    猜你喜欢
    • 1970-01-01
    • 2010-10-18
    • 2011-05-24
    • 2014-04-21
    • 2015-05-09
    • 1970-01-01
    • 2013-05-27
    • 1970-01-01
    • 2016-07-19
    相关资源
    最近更新 更多