我有一个关于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
会有区别?它必须与实习生池有关吗?或者我只是被误导了?
6条答案
按热度按时间xdyibdwo1#
当您将
String
(* 不可变 *)变量声明为final
,并使用编译时常量表达式初始化它时,它也会成为编译时常量表达式,其值由使用它的编译器内联。因此,在第二个代码示例中,内联值后,编译器会将字符串串联转换为:与
"string"
相比,它将给予true
,因为字符串常量是 interned。来自JLS §4.12.4 -
final
变量:基元类型或
String
类型的变量,即final
,用编译时常量表达式初始化(参见15.28节),称为 constant variable。另见JLS §15.28 -常量表达式:
String
类型的编译时常量表达式总是被“interned”,以便使用String#intern()
方法共享唯一的示例。在第一个代码示例中,
String
变量不是final
。因此,它们不是编译时常量表达式。其中的串联操作将延迟到运行时,从而导致创建新的String
对象。您可以通过比较两段代码的字节码来验证这一点。第一个代码示例**(非
final
版本)**编译为以下字节代码:显然,它将
str
和ing
存储在两个单独的变量中,并使用StringBuilder
执行连接操作。而第二个代码示例**(
final
版本)**如下所示:因此,它直接内联最后一个变量以在编译时创建String
string
,该变量在步骤0
中通过ldc
操作加载。然后,在步骤7
中通过ldc
操作加载第二个字符串文字。它不涉及在运行时创建任何新的String
对象。String在编译时是已知的。他们被拘留了。3ks5zfa02#
根据我的研究,所有的
final String
都是用Java编写的。所以,如果你真的需要使用==或!=来比较两个String,请确保在进行比较之前调用String.intern()方法。否则,总是首选String.equals(String)进行String比较。
这意味着如果你调用
String.intern()
你可以用==
运算符比较两个字符串,但是这里String.intern()
不是必需的,因为在Java中final String
是内部寄存的。您可以找到有关String.intern()方法的String comparision using == operator和Javadoc的更多信息。
也可以参考这篇Stackoverflow文章了解更多信息。
c2e8gylq3#
如果你看看这些方法
以及使用
javap -c ClassWithTheseMethods
版本进行反编译后,您将看到以及
因此,如果字符串不是最终编译器将不得不使用
StringBuilder
连接str1
和str2
,因此将被编译为
这意味着
concat
将在运行时创建,因此不会来自字符串池。此外,如果字符串是final,则编译器可以假定它们永远不会更改,因此可以安全地连接其值,而不是使用
StringBuilder
可改为
并连接到
这意味着
concate
将成为字符串常量,该字符串常量将被驻留在字符串池中,然后在if
语句中与来自该池的相同字符串常量进行比较。zqry0prt4#
堆栈和串容器池概念
djmepvbi5#
让我们看看
final
示例的一些字节代码在
0:
和2:
处,String
"string"
被压入堆栈(从常量池)并直接存储到局部变量concat
中,可以推断编译器在编译时创建(连接)了String
"string"
本身。非
final
字节代码这里有两个
String
常量,"str"
和"ing"
,它们需要在运行时与StringBuilder
连接。lztngnrs6#
但是,当您使用Java的String文字表示法创建时,它会自动调用intern()方法将该对象放入String池中,前提是该对象尚未出现在池中。
为什么期末考试会有不同?
编译器知道final变量永远不会改变,当我们添加这些final变量时,输出将进入字符串池,因为
str1 + str2
表达式输出也永远不会改变,因此final编译器在输出上述两个final变量后调用inter方法。在非final变量的情况下,编译器不调用intern方法。