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