我有两个问题:
public static void main(String[] args) {
String s1 = "bla";
String s2 = "b" +"l" + "a";
String s3 = "b".concat("l").concat("a");
if(s1 == s2)
System.out.println("Equal");
else
System.out.println("Not equal");
if(s1 == s3)
System.out.println("Equal");
else
System.out.println("Not equal");
}
为什么s1
和s2
指向同一个对象,而s1
和s3
却没有? (没有使用new
关键字)。
如果我从用户那里得到一个字符串并将上述代码添加到上面的代码中:
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
String name=in.readLine();
if(name.equals("test"))
s1 = s1 + "xyz";
如果用户输入xyz
,程序将打印Not equal
,当用户输入其他内容时,程序将输出Equal
。这是否意味着池通过执行整个程序而改变?优化程序是否在编译时工作,继续在runtime
中工作?
答案 0 :(得分:15)
为什么s1和s2指向同一个对象,而s1和s3不指向? (没有使用新关键字)。
因为连接发生在编译时,因此完成的字符串在常量池中与第一个示例中相同。这是编译器“已知”的特殊情况。它的确意味着长串,以这种方式连接多行,仍然可以获得与简单字符串常量相同的性能改进。
在第二个示例中,您在运行时执行计算,因此它不会成为常量池的一部分。
但是请注意,在JLS中,有意和无法进入字符串常量池的细节有意留下模糊,因此不同的实现可能会以不同的方式进行优化。它指定了具有的内容的某些规则,但不依赖于这种行为在各实现中保持一致。
答案 1 :(得分:9)
为什么s1和s2指向同一个对象,而s1和s3指向同一个对象 不? (没有使用新关键字)。
由于Java中的String
是Immutable
,因此字符串类的任何方法都将返回一个新的String对象(但有一些例外 - 一个是substring
方法)。因此concat
方法会创建一个新字符串,该字符串将转到堆,而不会添加到常量池中。
就s1
和s2
的情况而言,两个字符串在 compile 时都已知,因此它们是相同的字符串文字。
请注意第二个字符串中的连接操作: -
String s2 = "b" +"l" + "a";
在编译时计算,并且已知结果与第一个字符串相同,并且对常量池进行一次输入。
答案 2 :(得分:3)
有时候(当编译器显然字符串的值在运行时是什么时)编译器使用字符串池,在其他情况下它不会。
实际上您的代码不应该依赖于使用或不使用池的事实。
你不能总是运行main,所以如果你想看看你的String是否是从池中使用的,你可以用 javap 反编译代码,列表相对不言自明。