为什么Javac为最终字段插入Objects.requireNonNull(this)?

问题描述 投票:4回答:1

考虑以下课程:

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特定的行为。

java java-8 javac java-11 ecj
1个回答
0
投票

由于该字段不仅是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类型的命名成员字段的值。

© www.soinside.com 2019 - 2024. All rights reserved.