用 == 比较在 Java 中声明为 final 的字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19418427/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Comparing strings with == which are declared final in Java
提问by Tiny
I have a simple question about strings in Java. The following segment of simple code just concatenates two strings and then compares them with ==
.
我有一个关于 Java 字符串的简单问题。下面的简单代码段只是连接两个字符串,然后将它们与==
.
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 ==
).
比较表达式concat=="string"
返回false
那么明显(I明白之间的差值equals()
和==
)。
When these two strings are declared final
like so,
当这两个字符串final
像这样声明时,
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?
比较表达式concat=="string"
,在本例中返回true
。为什么会final
有所作为?它是否必须与实习生池有关,或者我只是被误导了?
采纳答案by Rohit Jain
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
(不可变的)变量声明为final
,并使用编译时常量表达式对其进行初始化时,它也会成为编译时常量表达式,并且它的值由使用它的编译器内联。因此,在您的第二个代码示例中,内联值后,字符串连接由编译器转换为:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
which when compared to "string"
will give you true
, because string literals are interned.
与 相比"string"
会给你true
,因为字符串文字是实习生的。
From JLS §4.12.4 - final
Variables:
A variable of primitive type or type
String
, that isfinal
and initialized with a compile-time constant expression (§15.28), is called a constant variable.
原始类型或类型的变量
String
,即final
用编译时常量表达式(第 15.28 节)初始化,称为常量变量。
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 methodString#intern()
.
类型的编译时常量表达式
String
总是“被嵌入”,以便使用方法共享唯一的实例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.
在您的第一个代码示例中,情况并非如此,其中String
变量不是final
. 因此,它们不是编译时常量表达式。那里的连接操作将延迟到运行时,从而导致创建新String
对象。您可以通过比较两个代码的字节码来验证这一点。
The first code example (non-final
version)is compiled to the following byte code:
第一个代码示例(非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
Clearly it is storing str
and ing
in two separate variables, and using StringBuilder
to perform the concatenation operation.
显然,它是将str
和存储ing
在两个单独的变量中,并StringBuilder
用于执行连接操作。
Whereas, your second code example (final
version)looks like this:
而您的第二个代码示例(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
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.
所以它string
在编译时直接内联final变量来创建String ,由ldc
step中的操作加载0
。然后通过ldc
step 中的操作加载第二个字符串文字7
。它不涉及String
在运行时创建任何新对象。String 在编译时就已经知道了,并且它们是实习的。
回答by Pradeep Simha
As per my research, all the final String
are interned in Java. From one of the blog post:
根据我的研究,所有这些final String
都在 Java 中实习。来自其中一篇博文:
So, if you really need to compare two String using == or != make sure you call String.intern() method before making comparison. Otherwise, always prefer String.equals(String) for String comparison.
因此,如果您确实需要使用 == 或 != 比较两个字符串,请确保在进行比较之前调用 String.intern() 方法。否则,总是更喜欢 String.equals(String) 进行字符串比较。
So it means if you call String.intern()
you can compare two strings using ==
operator. But here String.intern()
is not necessary because in Java final String
are internally interned.
所以这意味着如果你打电话,String.intern()
你可以使用==
运算符比较两个字符串。但这里String.intern()
没有必要,因为在 Java 中final String
是内部实习的。
You can find more information String comparision using == operatorand Javadoc for String.intern()method.
您可以使用 == 运算符和 Javadoc for String.intern()方法找到更多信息字符串比较。
Also refer this Stackoverflowpost for more information.
另请参阅此Stackoverflow帖子以获取更多信息。
回答by Pshemo
If you take a look at this methods
如果你看看这个方法
public void noFinal() {
String str1 = "str";
String str2 = "ing";
String concat = str1 + str2;
System.out.println(concat == "string");
}
public void withFinal() {
final String str1 = "str";
final String str2 = "ing";
String concat = str1 + str2;
System.out.println(concat == "string");
}
and its decompiled with javap -c ClassWithTheseMethods
versions you will see
并使用javap -c ClassWithTheseMethods
您将看到的版本进行反编译
public void noFinal();
Code:
0: ldc #15 // String str
2: astore_1
3: ldc #17 // String ing
5: astore_2
6: new #19 // class java/lang/StringBuilder
9: dup
10: aload_1
11: invokestatic #21 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #27 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: invokevirtual #30 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
...
and
和
public void withFinal();
Code:
0: ldc #15 // String str
2: astore_1
3: ldc #17 // String ing
5: astore_2
6: ldc #44 // String string
8: astore_3
...
So if Strings are not final compiler will have to use StringBuilder
to concatenate str1
and str2
so
所以,如果串不是最终的编译器将不得不使用StringBuilder
来连接str1
和str2
使
String concat=str1+str2;
will be compiled to
将被编译为
String concat = new StringBuilder(str1).append(str2).toString();
which means that concat
will be created at runtime so will not come from String pool.
这意味着concat
将在运行时创建,因此不会来自字符串池。
Also if Strings are final then compiler can assume that they will never change so instead of using StringBuilder
it can safely concatenate its values so
此外,如果字符串是最终的,那么编译器可以假设它们永远不会改变,因此StringBuilder
可以安全地连接它的值而不是使用它
String concat = str1 + str2;
can be changed to
可以改为
String concat = "str" + "ing";
and concatenated into
并连接成
String concat = "string";
which means that concate
will become sting literal which will be interned in string pool and then compared with same string literal from that pool in if
statement.
这意味着concate
它将成为字符串池中的字符串,然后与if
语句中该池中的相同字符串进行比较。
回答by Sotirios Delimanolis
Let's see some byte code for the final
example
让我们看看这个final
例子的一些字节码
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
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
}
At 0:
and 2:
, the String
"string"
is pushed onto the stack (from the constant pool) and stored into the local variable concat
directly. You can deduce that the compiler is creating (concatenating) the String
"string"
itself at compilation time.
在0:
和 处2:
,String
"string"
被压入堆栈(来自常量池)并concat
直接存储到局部变量中。您可以推断编译器正在编译时创建(连接)String
"string"
自身。
The non final
byte code
非final
字节码
Compiled from "Main2.java"
public class Main2 {
public Main2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
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/Stri
ngBuilder;
17: aload_2
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
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
}
Here you have two String
constants, "str"
and "ing"
which need to be concatenated at runtime with a StringBuilder
.
在这里,你有两个String
常量,"str"
而"ing"
这就需要在运行时用一个级联StringBuilder
。
回答by pnathan
Stack and string conts pool concept
堆栈和字符串 conts 池概念
回答by Premraj
Though, when you create using String literal notation of Java, it automatically call intern() method to put that object into String pool, provided it was not present in the pool already.
但是,当您使用 Java 的字符串文字表示法创建时,它会自动调用 intern() 方法将该对象放入字符串池中,前提是该对象尚未出现在池中。
Why does final make a difference?
为什么 final 会有所不同?
Compilerknows the final variable never gonna change, when we add these final variables the output goes to String Pool because of str1 + str2
expression output also never gonna change, So finally compiler calls inter method after output of the above two final variables. In case of non-final variable compiler do not call intern method.
编译器知道最终变量永远不会改变,当我们添加这些最终变量时,输出会进入字符串池,因为str1 + str2
表达式输出也永远不会改变,所以最终编译器在输出上述两个最终变量后调用 inter 方法。在非最终变量编译器的情况下,不要调用实习生方法。