为什么 javac 为最终字段插入 Objects.requireNonNull(this)?
Why does javac insert Objects.requireNonNull(this) for final fields?
考虑以下 class:
class Temp {
private final int field = 5;
int sum() {
return 1 + this.field;
}
}
然后我编译和反编译 class:
> 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)
在这里做什么?重点是什么?这与可达性有某种联系吗?
Java8编译器类似。它插入 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
指令在你的情况下。
但是必须保留在取消引用 null
时抛出 NullPointerException
的行为,这在使用 getfield
指令时会被暗示。因此,当您将方法更改为
int sumA() {
Temp t = this;
return 1 + t.field;
}
Eclipse 也会插入显式空检查。
所以我们在这里看到的是 javac
无法识别在这种特定情况下,当引用为 this
时,非空 属性 由 JVM 保证因此,显式空检查是不必要的。
¹ 见 JLS §15.11.1. Field Access Using a Primary:
If the field is not static
:
- The Primary expression is evaluated. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.
- If the value of the Primary is
null
, then a NullPointerException
is thrown.
- If the field is a non-blank
final
, then the result is the value of the named member field in type T
found in the object referenced by the value of the Primary.
…
考虑以下 class:
class Temp {
private final int field = 5;
int sum() {
return 1 + this.field;
}
}
然后我编译和反编译 class:
> 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)
在这里做什么?重点是什么?这与可达性有某种联系吗?
Java8编译器类似。它插入 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
指令在你的情况下。
但是必须保留在取消引用 null
时抛出 NullPointerException
的行为,这在使用 getfield
指令时会被暗示。因此,当您将方法更改为
int sumA() {
Temp t = this;
return 1 + t.field;
}
Eclipse 也会插入显式空检查。
所以我们在这里看到的是 javac
无法识别在这种特定情况下,当引用为 this
时,非空 属性 由 JVM 保证因此,显式空检查是不必要的。
¹ 见 JLS §15.11.1. Field Access Using a Primary:
If the field is not
static
:
- The Primary expression is evaluated. If evaluation of the Primary expression completes abruptly, the field access expression completes abruptly for the same reason.
- If the value of the Primary is
null
, then aNullPointerException
is thrown.- If the field is a non-blank
final
, then the result is the value of the named member field in typeT
found in the object referenced by the value of the Primary.…