Java陷阱:内部类中的字段访问

这本身不是一个“陷阱”,而是一个值得了解的实现细节。 假设我有一个带有字段的内部类。 这样的字段对于封闭的类是可见的,但是以下哪种方法是访问它的最快方法? 注意! 我只在这里查看生成的字节码,而不考虑任何JIT优化,因此这种“性能分析”非常幼稚
public class Test {

  public void testAlternatives() {
    // Alternative 1
    System.out.println(new Inner().field);
    // Alternative 2
    System.out.println(new Inner().getField());
    // Alternative 3
    System.out.println(new Inner2().field);
    // Alternative 4
    System.out.println(new Inner2().getField());
  }

  class Inner {
    private int field;

    public int getField() {
      return field;
    }
  }

  class Inner2 {
    int field;

    public int getField() {
      return field;
    }
  }

}

一个直观的答案是,替代方案1和3的速度相当快,因为​​该字段始终对封闭的类可见,并且两者都使用字段访问,总体而言,该字段的访问速度比替代方案2和4中使用的方法访问快一点。但是,有一个实现细节导致这是不正确的。 JVM本身没有称为“内部类”的概念。 整个概念由Java编译器实现,并且在字节码级别,所有内容均由普通类组成。

这里的问题是,如果内部类具有私有字段,则编译器最终会将内部类编译为普通类。 普通类中的私有字段不能被其他类访问,因此,封闭的Test类如果没有技巧,就无法“看到”该字段。 这是上面的代码“拒绝”了编译器实际编译为字节码的内容:

public class Test {
  public void testAlternatives() {
    // Alternative 1
    System.out.println(Test$Inner.access$000(new Test$Inner(this)));
    // Alternative 2
    System.out.println(new Test$Inner(this).getField());
    // Alternative 3
    System.out.println(new Test$Inner2(this).field);
    // Alternative 4
    System.out.println(new Test$Inner2(this).getField());
  }
}

class Test$Inner {
  final Test this$0;

  private int field;

  Test$Inner(Test test) {
    this$0 = test;
  }

  public int getField() {
    return field;
  }

  static int access$000(Test$Inner inner) {
    return inner.field;
  }

}

class Test$Inner2 {
  final Test this$0;

  int field;

  Test$Inner2(Test test) {
    this$0 = test;
  }

  public int getField() {
    return field;
  }

}

如您所见,为了授予对私有字段的访问权限,将生成一个名为access $ 000的程序包级静态访问器方法。 现在,更容易看到替代方法3最有可能成为最快的替代方法,因为它是唯一使用直接字段访问的方法。 在字段中使用程序包访问是一个微优化,但是这绝对是Java开发人员应该知道的一个细节。 在对性能至关重要的代码部分中,这实际上可能很重要,而Android性能指南实际上提到了此实现细节。

当尝试对内部类的空引用进行字段访问时,此实现的详细信息也可能会引起一些混乱。 考虑以下代码:

public class NullTest {
  class Inner {
    private int field;
  }

  public void test() {
    Inner inner = null;
    System.out.println(inner.field);
  }

  public static void main(String[] args) {
    new NullTest().test();
  }
}

变量“ inner”为空,因此显然会引发NullPointerException。 但是,从原始代码中看不出来的是,异常是在编译器生成的静态访问器方法内引发的!

$ java NullTest
Exception in thread 'main' java.lang.NullPointerException
 at NullTest$Inner.access$000(NullTest.java:2)
 at NullTest.test(NullTest.java:8)
 at NullTest.main(NullTest.java:12)

堆栈跟踪包含直观的异常源(第8行),但是实际源会使不了解编译器生成的访问器方法的开发人员感到困惑。

参考: Java陷阱:来自Jawsy Solutions技术博客博客的JCG合作伙伴 Joonas Javanainen, 内部类中的字段访问


翻译自: https://www.javacodegeeks.com/2012/05/java-pitfalls-field-access-in-inner.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值