用 == 比较在 Java 中声明为 final 的字符串 [英] Comparing strings with == which are declared final in Java

查看:30
本文介绍了用 == 比较在 Java 中声明为 final 的字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个关于 Java 字符串的简单问题.下面这段简单的代码只是将两个字符串连接起来,然后将它们与 == 进行比较.

I have a simple question about strings in Java. The following segment of simple code just concatenates two strings and then compares them with ==.

String str1="str";
String str2="ing";
String concat=str1+str2;

System.out.println(concat=="string");

比较表达式 concat=="string" 返回 false 很明显(我理解 equals() 之间的区别==).

The comparison expression concat=="string" returns false as obvious (I understand the difference between equals() and ==).

当这两个字符串像这样声明final时,

When these two strings are declared final like so,

final String str1="str";
final String str2="ing";
String concat=str1+str2;

System.out.println(concat=="string");

比较表达式concat=="string",在这种情况下返回true.为什么 final 会有所作为?它是否必须与实习生池有关,还是我只是被误导了?

The comparison expression concat=="string", in this case returns true. Why does final make a difference? Does it have to do something with the intern pool or I'm just being misled?

推荐答案

当你将 String(它是不可变)变量声明为 final,并用编译时常量表达式初始化它,它也成为编译时常量表达式,它的值由编译器在使用它的地方内联.因此,在您的第二个代码示例中,内联值后,字符串连接被编译器转换为:

When you declare a String (which is immutable) variable as final, and initialize it with a compile-time constant expression, it also becomes a compile-time constant expression, and its value is inlined by the compiler where it is used. So, in your second code example, after inlining the values, the string concatenation is translated by the compiler to:

String concat = "str" + "ing";  // which then becomes `String concat = "string";`

"string" 相比,它会给你 true,因为字符串文字是 interned.

which when compared to "string" will give you true, because string literals are interned.

来自 JLS §4.12.4- final 变量:

From JLS §4.12.4 - final Variables:

原始类型或 String 类型的变量,即 final 并使用编译时常量表达式(第 15.28 节)初始化,称为 常量变量.

A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.

也来自 JLS §15.28 - 常量表达式:

String 类型的编译时常量表达式总是 "interned" 以便共享唯一的实例,使用方法 String#intern().

Compile-time constant expressions of type String are always "interned" so as to share unique instances, using the method String#intern().


在您的第一个代码示例中,情况并非如此,其中 String 变量不是 final.因此,它们不是编译时常量表达式.那里的连接操作将延迟到运行时,从而导致创建一个新的 String 对象.您可以通过比较两个代码的字节码来验证这一点.


This is not the case in your first code example, where the String variables are not final. So, they are not a compile-time constant expressions. The concatenation operation there will be delayed till runtime, thus leading to the creation of a new String object. You can verify this by comparing byte code of both the codes.

第一个代码示例(非final版本)被编译为以下字节码:

The first code example (non-final version) is compiled to the following byte code:

  Code:
   0:   ldc     #2; //String str
   2:   astore_1
   3:   ldc     #3; //String ing
   5:   astore_2
   6:   new     #4; //class java/lang/StringBuilder
   9:   dup
   10:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   13:  aload_1
   14:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_2
   18:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   24:  astore_3
   25:  getstatic       #8; //Field java/lang/System.out:Ljava/io/PrintStream;
   28:  aload_3
   29:  ldc     #9; //String string
   31:  if_acmpne       38
   34:  iconst_1
   35:  goto    39
   38:  iconst_0
   39:  invokevirtual   #10; //Method java/io/PrintStream.println:(Z)V
   42:  return

显然它是将string存储在两个独立的变量中,并使用StringBuilder来执行连接操作.

Clearly it is storing str and ing in two separate variables, and using StringBuilder to perform the concatenation operation.

然而,您的第二个代码示例(final 版本) 如下所示:

Whereas, your second code example (final version) looks like this:

  Code:
   0:   ldc     #2; //String string
   2:   astore_3
   3:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   6:   aload_3
   7:   ldc     #2; //String string
   9:   if_acmpne       16
   12:  iconst_1
   13:  goto    17
   16:  iconst_0
   17:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
   20:  return

所以它在编译时直接内联final变量创建Stringstring,由0中的ldc操作加载.然后在步骤 7 中通过 ldc 操作加载第二个字符串文字.它不涉及在运行时创建任何新的 String 对象.String 在编译时就已经知道了,并且它们被实习了.

So it directly inlines the final variable to create String string at compile time, which is loaded by ldc operation in step 0. Then the second string literal is loaded by ldc operation in step 7. It doesn't involve creation of any new String object at runtime. The String is already known at compile time, and they are interned.

这篇关于用 == 比较在 Java 中声明为 final 的字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆