这个问题在这里已经有了答案:
如何在java中连接最终字符串(1个答案)
26天前关门了。
我有以下代码。我理解java字符串不变性和字符串常量池的概念。我不明白为什么在下面的程序中'name1==name2'结果为false,'name2==name3'结果为true。字符串变量name1、name2和name3是如何放置在字符串常量池中的?
public class Test {
public static void main(String[] args) {
final String firstName = "John";
String lastName = "Smith";
String name1 = firstName + lastName;
String name2 = firstName + "Smith";
String name3 = "John" + "Smith";
System.out.println(name1 == name2);
System.out.println(name2 == name3);
}
}
Output:
false
true
3条答案
按热度按时间rwqw0loc1#
跑
javap -c Test
在你编译代码之后,你会看到的
如你所见
在
public static void main(java.lang.String[]);
,这是运行时实际编译的字节码firstname + lastname
. 因此生成的字符串将不会与常量池中的“johnsmith”具有相同的哈希代码。在哪里
和
这是编译器在执行这两种操作时生成的字节码
firstname + "Smith"
以及"John" + "Smith"
这意味着两者实际上都是从常量池中读取的。这就是为什么在比较name1和name2时
==
它会回来的false
. 自name2
以及name3
从常量池中引用相同的字符串。亨克它回来了true
与…相比==
. 这就是为什么比较2字符串不是一个好主意的原因==
. 请使用String.equals()
进行字符串比较时。因为两者
bq3bfh9z2#
让我们看看字节码
final
:字节码没有
final
:正如你所看到的
final
,java可以识别+
所以它用常量字符串替换串联"JohnSmith"
在编译时。唯一的要求makeConcatWithConstants
(连接字符串)是为firstName + lastName
,自lastName
不是最终决定。在第二个示例中,有两个调用
makeConcatWithConstants
,一个firstName + lastName
再来一杯firstName + "Smith"
,因为java无法识别firstName
作为常量。这就是为什么
name1 == name2
是false
在您的示例中:name2
是一个常数"JohnSmith"
在字符串池中name1
在运行时动态计算。然而,name2
以及name3
这两个常量都在字符串池中,这就是为什么name2 == name3
是true
.nkkqxpd93#
答案相当简单:作为一种捷径,java将某些概念视为所谓的“编译时常量”(compiletime constant,ctc)。其思想是在编译级别完全内联一个变量(这很特别;正常情况下
javac
基本上,只需使用非常简单且易于理解的转换将java源文件猛击为类文件,fancypants优化在运行时进行(hotspot)。例如,如果您这样做:
保存到
UserOfBatch.java
:在命令行上运行:
看看上面的3号线。
iconst_5
. 那5个?它是硬编码的!!未提及
BatchOConstants
剩下的。让我们测试一下:在命令行上:
真 的。代码运行时,即使它丢失了应该提供5的类文件,因此证明它是由编译器本身而不是运行时“硬编码”的。
“观察”任何值的真实性的另一种方法是注解图。注解参数必须由javac硬编码到类文件中,因此不能传递表达式。鉴于:
我不会写:
@Foo(System.currentTimeMillis())
,因为System.cTM
显然不是编译时常量。但我会写作@Foo(SomeClass.SOME_STATIC_FINAL_LONG_FIELD)
假设分配给s\u s\u f\u l\u f的值是编译时常量。如果不是,那@Foo(...)
代码无法编译。如果是,它将编译:现在决定代码是否编译。关于何时允许编译器将某个内容解析为“编译时常量”并进行内联狂欢,有一些特定的规则。例如,
null
永远不是内联常量。过于简单化,但:对于字段,字段必须是静态的和final的,并且具有一个原语或字符串类型,该原语或字符串类型使用一个不为null的常量表达式当场初始化(同时,不要稍后在静态块中初始化)。
对于局部变量,规则是非常相似的,不同的是,它们必须是静态的,因为它们不能是静态的。除此之外,所有的fixin都适用:final、primitive或string、non-null、当场初始化并使用常量表达式。
不幸的是,一个地方的真实性很难直接观察到。不过,您编写的代码是间接观察它的好方法。你的指纹证明了
firstName
ctc和lastName
不是。不过,我们可以观察到一些精选的东西。所以让我们把你的代码,编译,然后扔到javap上,看看结果。通过jonas konrad的在线javap工具,让我们分析一下:
相关部分:
注意“启动本地2”(即c;javap从0开始计数)显示正在加载
hello!
作为一个完整的常量,但“start local 3”(即d)显示加载2个常量并调用makeconcat将它们连接在一起。