将字符串与==中的Java进行比较(Comparing strings with == which are declared final in Java)

编程入门 行业动态 更新时间:2024-10-16 22:12:58
字符串与==中的Java进行比较(Comparing strings with == which are declared final in Java)

我有一个关于Java中的字符串的简单问题。 以下段的简单代码只是连接两个字符串,然后将它们与==比较。

String str1="str"; String str2="ing"; String concat=str1+str2; System.out.println(concat=="string");

比较表达式concat=="string"返回false是明显的(我明白equals()和==之间的区别)。


当这两个字符串被这样声明为final ,

final String str1="str"; final String str2="ing"; String concat=str1+str2; System.out.println(concat=="string");

比较表达式concat=="string" ,在这种情况下返回true 。 为什么final有所作为? 是否必须在实习池中做某事,或者我只是被误导?

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");

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


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");

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 ,并且用一个编译时的常量表达式来初始化它时,它也变成一个编译时的常量表达式,它的值被编译器所使用的内联。 因此,在第二个代码示例中,在内联值之后,编译器将字符串连接转换为:

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

当与"string"相比时,它会给你true ,因为字符串文字是被内联的

从JLS§4.12.4 - final变量 :

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

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

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


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

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

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

显然,将str和ing存储在两个单独的变量中,并使用StringBuilder来执行连接操作。

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

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

所以它直接内联最终的变量,在编译时创建String string ,它由ldc操作在步骤0加载。 然后在步骤7通过ldc操作加载第二个字符串文字。 它不涉及在运行时创建任何新的String对象。 字符串在编译时已经知道,它们被实体化。

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";`

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

From JLS §4.12.4 - final Variables:

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.

Also from JLS §15.28 - Constant Expression:

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


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.

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

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

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

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.

更多推荐

本文发布于:2023-08-07 15:53:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1464541.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:字符串   Java   Comparing   final   declared

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!