final String和final Integer的区别

4

我的Java类

private static final String constantString = "Constant";    
private static final Integer constantInteger = 5;
public static void main(String[] args) {
    String s2 = constantString + "append"; // LINENUMBER 9
    Integer i2 = constantInteger + 7; // LINENUMBER 10
}

字节码
 LINENUMBER 9 L0
    LDC "Constantappend"
    ASTORE 1
   L1
    LINENUMBER 10 L1
    GETSTATIC TestClass.constantInteger : Ljava/lang/Integer;
    INVOKEVIRTUAL java/lang/Integer.intValue()I
    BIPUSH 7
    IADD

问题1:为什么编译器不会用5替换final Integer(constantInteger)的值,但对于String却可以!

如果删除Integer变量的final关键字

Java代码:

private static Integer constantInteger = 5;

字节码:

LINENUMBER 10 L1
GETSTATIC TestClass.constantInteger : Ljava/lang/Integer;
INVOKEVIRTUAL java/lang/Integer.intValue()I
BIPUSH 7

两种情况下字节码相同(静态final Integer,静态Integer)

问题2:那么将Integer定义为final有什么用?


2
那么将整数声明为final有什么用呢?编译器不允许您更改其值。 - leonbloy
4个回答

6
推测原因是字符串插入。编译器将字符串插入并优化插入的字符串连接,但它不会将数字插入,并且显然缺乏这种优化。我想不出为什么不能优化整数情况的任何原因,除了在编译器中还没有实现。如其他答案中所述,整数+涉及装箱/拆箱操作,与字符串+及其隐式StringBuilder优化和插入逻辑大不相同。因此,从编译器实现的角度来看,字符串+优化可能是“免费”的。

5
编译器没有注意到具有非空值的最终整数必然是非空的。因此,它继续对其进行拆箱处理。 JIT编译器完全有可能在转换为机器代码的过程中改进这一点;无论如何,这似乎都不会影响您的应用程序性能。
“final”关键字是关于语义而非性能。语言保证了语义,性能完全取决于实现者的心情。因此,明确回答“问题2”的问题:final的目的是具有防止修改的语义。还请注意嵌入式匿名类的规则,其中它们仅可以使用final局部变量。

3
短答案:尝试使用static final int constantInteger = 5
编译器只能替换常量表达式。Java具有所谓的基本值,即以小写字符开头的类型(long、int、char等)。这些值不是对象,因此它们没有状态并且可以直接替换。
作为一个优化,编译器也可以将String对象视为常量。在运行时,String只是保存字符数组的容器,但在编译时,编译器假装String只是字符数据。然而,String是唯一允许这种情况的类。
出于类似的原因,在注释中可以使用String和Class,但不能使用其他对象类型作为值。

1

来自Java语言规范,v3

由常量表达式(§15.28)计算得出的字符串在编译时计算,然后被视为字面量。

您的字符串表达式由两个字面量组成,在编译时计算为单个字面量。整数没有这样的处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接