字符串常量池与字符串池
我对这两件事很困惑。 我需要帮助。 请清楚我的疑问,String Constant Pool和String pool是否都是相同的概念。 我在面试中遇到了这个问题。 我已经阅读了很多网站和博客,但我的疑问还没有清除。请清除我的疑虑。
提前致谢。
两者都是一回事。 String Constant Pool包含常量字符串对象。 可以将Constant
定义为String对象在编译时保存该值。 有关更多信息,请参阅JLS
String s="abc"; String s1="def"; String s2=s+"def"; String s3="abc"+"def"; System.out.println(s2==s3); // print false
但是,如果你最后决定
final String s="abc"; String s1="def"; String s2=s+"def"; String s3="abc"+"def"; System.out.println(s2==s3); // print true
在上面的情况中, s3
是编译时常数,因为s是最终的。
字符串池(=“字符串常量池”):
-
这是String的类级/静态实习存储的非正式昵称。 注意:javadoc提到“字符串池” http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#intern%28%29 。
-
它是一个包含在应用程序执行期间实现的每个唯一String值的集合。 对于所有编译时字符串常量(文字和固定表达式)以及调用
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
这意味着String变量只能通过引用具有新值的新String对象来更改值 – 当然,这是由编译器和JVM在内部管理的。 这也意味着池中的所有项都是String常量 。
常量池(不专注于字符串,但确实包括字符串):
- 存在于每个类文件中(并且在内存中,对于每个加载的类)。 是否使用常量(即最终变量)的每类记录。 http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4 http://docs.oracle.com/javase/specs/jvms/se7/html/jvms -2.html#的JVM-2.5.5
我考虑过它并且我不确定但是字符串池可能引用了字符串文字池,比如String apple = "apple";
其中String常量池可以引用常量字符串对象,就像那些使用关键字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框架中。
- 字符串文字始终引用类String的相同实例。
- 任何包的任何类中的文字字符串表示对同一String对象的引用。
- 由常量表达式计算的字符串在编译时计算,然后将其视为文字。
例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”将保存在String池中。
"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."":(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不变。
让我们比较上面代码的字节代码;
决赛”
21: ldc #8 // String abc 23: invokespecial #27 // Method java/lang/StringBuilder."":(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;
没有决赛”
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."":(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;
和
决赛”
39: aload_0 40: ldc #40 // String abcdef 42: putfield #42 // Field s3:Ljava/lang/String;
没有决赛”
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."":(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;
该比较还certificate在比较时替换最终变量的值。 并且常量字段保存到CP中。 因此,如果’s’不是最终的那么’s的值是使用getfield从CP中获取的。