2

在 java 语言中,final 字段在初始化时采用立即值并且不能再更改。在 java 字节码 (jasmin) 中,如果我创建一个 final 字段,它会忽略我在初始化时分配给它的立即值,我可以稍后像任何其他变量一样更改它。

前任。爪哇代码:

public class App{
    final int CONST = 2;
    App(){
        CONST = 3;
    }
    public static void main(String[] args){
        App app = new App();
    }
}

输出:

App.java:4 error: cannot assign a value to final variable CONST

前任。茉莉字节码:

.class App
.super java/lang/Object

.field private final CONST I = 2 ;!!! the immediate value is ignored, 0 assigned

.method public <init>()V
    .limit stack 3
    .limit locals 1
    aload_0
    invokespecial java/lang/Object/<init>()V

    aload_0
    bipush 3
    putfield App/CONST I ;!!! overwritting final field

    return
.end method

.method public static main([Ljava/lang/String;)V
    .limit stack 1
    .limit locals 1

    new App
    invokespecial App/<init>()V

    return
.end method

输出:

Generated: App.class

没有错误?我还测试了打印出新的 CONST 值,它就像一个普通变量一样工作。为什么 final 字段不像在 java 代码中那样工作?

4

1 回答 1

2

Java 语言强制执行了许多在字节码级别未强制执行的约束。其中之一是处理最终字段。

在 Java 字节码中,对 final 字段的唯一限制是静态 final 字段不能分配到<clinit>方法之外,非静态 final 字段不能分配到<init>方法之外(即构造函数)。

您可以分配到最终字段 0、1、2 或任意次数。如果您链接构造函数,您可以在一个构造函数中分配给它,然后在另一个构造函数中覆盖它。

要了解有关字节码如何工作的更多信息,您应该阅读 JVM 规范。

附带说明一下,尽管语法看似相似,但以下内容完全不同。

final int CONST = 2;

.field private final CONST I = 2 ;!!! the immediate value is ignored, 0 assigned

在第一个示例中,这是一个(非静态)初始化程序。所有初始化程序的主体实际上只是复制粘贴到每个构造函数中。所以你得到的效果就像你CONST = 2在构造函数中写的一样。

相比之下,Jasmin 语法是创建一个ConstantValue属性。这通常用于为静态最终字段提供初始值。可以为任何字段指定它,但对于非静态字段会被忽略,因此您会看到该值被忽略。

于 2016-02-10T16:27:02.060 回答