考虑以下课程:
class Temp {
private final int field = 5;
int sum() {
return 1 + this.field;
}
}
然后我编译和反编译该类:
> javac --version
javac 11.0.5
> javac Temp.java
> javap -v Temp.class
...
int sum();
descriptor: ()I
flags: (0x0000)
Code:
stack=2, locals=1, args_size=1
0: iconst_1
1: aload_0
2: invokestatic #3 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
5: pop
6: iconst_5
7: iadd
8: ireturn
简单来说,javac
编译为sum()
:
int sum() {
final int n = 1;
Objects.requireNonNull(this); // <---
return n + 5;
}
Objects.requireNonNull(this)
在这里做什么?重点是什么?这以某种方式与可达性有关吗?
Java 8编译器与此类似。它插入this.getClass()
而不是Objects.requireNonNull(this)
:
int sum() {
final int n = 1;
this.getClass(); // <---
return n + 5;
}
我还尝试使用Eclipse进行编译。它不插入requireNonNull
:
int sum() {
return 1 + 5;
}
所以这是javac特定的行为。
由于该字段不仅是final
,而且是编译时常量,因此在读取时将不会访问该字段,但是读取将被常量值本身(您的iconst_5
指令)替换。情况。
但是必须保留在引用NullPointerException
时抛出null
的行为,这在使用getfield
指令时可能隐含¹。因此,当您将方法更改为
int sumA() {
Temp t = this;
return 1 + t.field;
}
Eclipse也将插入一个显式的null检查。
所以我们在这里看到的是javac
无法识别在这种特定情况下,当引用为this
时,JVM保证了非null属性,因此,不需要显式的null检查。
¹参见JLS §15.11.1. Field Access Using a Primary:
如果该字段不是
static
:
- Primary表达式被求值。如果Primary表达式的求值突然完成,则字段访问表达式由于相同的原因而突然完成。
- 如果Primary的值为
null
,则抛出NullPointerException
。- 如果该字段是非空白
final
,则结果为在由Primary的值引用的对象中找到的T
类型的命名成员字段的值。…