我对这两件事感到困惑。我需要帮助。请清除我的疑问,字符串常量池和字符串池是否是同一个概念。我在面试时遇到了这个问题。我已经阅读了很多网站和博客,但是我的疑问没有被清除。请清除我的疑问。
提前致谢。
我对这两件事感到困惑。我需要帮助。请清除我的疑问,字符串常量池和字符串池是否是同一个概念。我在面试时遇到了这个问题。我已经阅读了很多网站和博客,但是我的疑问没有被清除。请清除我的疑问。
提前致谢。
两者都是一回事。字符串常量池包含常量字符串对象。Constant
可以定义为 String 对象在编译时保存该值。有关更多信息,请参阅JLS。
String s="abc";
String s1="def";
String s2=s+"def";
String s3="abc"+"def";
System.out.println(s2==s3); // print false
但是,如果您将s
其设为最终版本
final String s="abc";
String s1="def";
String s2=s+"def";
String s3="abc"+"def";
System.out.println(s2==s3); // print true
在上述情况下s3
,是一个编译时间常数,因为 s 是 final 。
字符串池(=“字符串常量池”):
这是 String 的类级/静态实习生存储的非正式昵称。注意:javadoc 提到“字符串池” http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#intern%28%29。
它是一个集合,其中包含在应用程序执行期间留存的每个唯一字符串值。对于所有编译时字符串常量(文字和固定表达式)以及String.intern()
调用的所有运行时字符串值,都会自动进行实习。JLS 要求 String 类型的编译时常量表达式始终是“内部的”,以便使用 String.intern 方法共享唯一实例。 http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28
JVM 规范不要求对象有任何特定的内部结构。 http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.7
Java 语言规范确实要求 String 对象具有常量(不变)值。http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.3.3
这意味着一个字符串变量只能通过引用一个新的字符串对象来改变值价值——当然,这是由编译器和 JVM 内部管理的。这也意味着池中的所有项目都是字符串常量。
常量池(不专注于字符串,但确实包括字符串):
我想了想,但我不确定,但字符串池可能指的是字符串文字池,比如String apple = "apple";
字符串常量池可能指的是常量字符串对象,比如使用关键字 final 的对象,尽管收到一个问题如果我在面试中遇到这种棘手的语义,我会很生气
字符串池(字符串常量/内存/文字池)与常量池
当编译器遇到任何字符串文字时,编译器会将其放入字符串常量池中。所有方法或类变量都引用该字符串常量池;
class MemoryModel { final String s = "abc"; String s5 = "abc";}
:
String s1 = "abc";
MemoryModel mm = new MemoryModel();
System.out.println("abc".hashCode()); //12345
System.out.println(mm.s.hashCode()); //12345
System.out.println(mm.s5.hashCode()); //12345
System.out.println(s1.hashCode()); //12345
字符串“abc”将进入字符串池,而 s,s5 将进入 MemoryModel 类的常量池(或运行时常量池)。's1' 是方法局部变量,因此它将保存在 JVM 框架中。
示例 2
public method(){
final String s="abc";
String s1="def";
final String s2=s+s1;
String s3=s+"def";
String s4="abc"+"def";
}
对于上述方法,类常量池中不会保存任何内容。“abc”、“def”和“abcdef”将保存在字符串池中。
"abcdef".hashCode() == ("abc" + "def").hashCode() //true
上述方法的字节码;
0: ldc #19 // String abc
2: astore_1
3: ldc #21 // String def
5: astore_2
6: new #23 // class java/lang/StringBuilder
9: dup
10: ldc #19 // String abc
12: invokespecial #25 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
15: astore_3
16: aload_3
17: aload_2
18: invokevirtual #28 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #32 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore 4
26: ldc #36 // String abcdef
28: astore 5
30: ldc #36 // String abcdef
32: astore 6
34: return
以上字节码表明没有将任何内容放入 CP(常量池)中。对于上面的代码 s3 == s4 和 s3 == “abcdef”,因为所有最终常量在编译时都替换为它们的值。所以上面的代码会转换成这个;
final String s="abc";
String s1="def";
final String s2=new StringBuilder("abc").append(s1).toString();
String s3="abc"+"def";
String s4="abc"+"def";
但如果 's' 不是最终的,那么
String s="abc";
String s1="def";
final String s2=new StringBuilder(s).append(s1).toString();
String s3=new StringBuilder(s).append("def").toString();
String s4="abc"+"def";
在上面的代码中,s2 和 s3 将指向一个具有 char[] 的 String 的新实例。所以 s3 和 s4 是不一样的。
示例 3
public class MemoryModel {
final String s="abc";
String s1="def";
final String s2=s+s1;
String s3=s+"def";
String s4="abc"+"def";
String s5=s2;
}
这个字节码与上面的字节码非常相似。但是你会看到更多的条目putfield
并且getfield
告诉我们常量被放入了 CP。
让我们比较一下上述代码的字节码;
带决赛的's'
21: ldc #8 // String abc
23: invokespecial #27 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
26: aload_0
27: getfield #23 // Field s1:Ljava/lang/String;
30: invokevirtual #30 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: putfield #38 // Field s2:Ljava/lang/String;
没有最终的's'
21: aload_0
22: getfield #18 // Field s:Ljava/lang/String;
25: invokestatic #26 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
28: invokespecial #32 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
31: aload_0
32: getfield #22 // Field s1:Ljava/lang/String;
35: invokevirtual #35 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
38: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
41: putfield #43 // Field s2:Ljava/lang/String;
和
带决赛的's'
39: aload_0
40: ldc #40 // String abcdef
42: putfield #42 // Field s3:Ljava/lang/String;
没有最终的's'
44: aload_0
45: new #24 // class java/lang/StringBuilder
48: dup
49: aload_0
50: getfield #18 // Field s:Ljava/lang/String;
53: invokestatic #26 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
56: invokespecial #32 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
59: ldc #20 // String def
61: invokevirtual #35 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
64: invokevirtual #39 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
67: putfield #45 // Field s3:Ljava/lang/String;
这个比较也证明了final变量的值在比较的时候被替换了。常量字段保存到 CP 中。因此,如果 's' 不是最终的,则 's 的值是使用 getfield 从 CP 获取的。