字符串常量池与字符串池

时间:2013-05-28 04:39:09

标签: java

我对这两件事感到困惑。我需要帮助。请清楚我的疑问,String Constant Pool和String pool是否都是相同的概念。我在采访中遇到了这个问题。我已经阅读了很多网站和博客但是,我的疑问还没有清除。请清除我的疑虑。

先谢谢。

4 个答案:

答案 0 :(得分:4)

两者都是一回事。 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

但如果你将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是最终的。

答案 1 :(得分:3)

字符串池(=“字符串常量池”):

常量池(不专注于字符串,但确实包含字符串):

答案 2 :(得分:2)

我考虑过它并且我不确定但是字符串池可能引用字符串文字池,像String apple = "apple";这样的东西,因为String常量池可能引用常量字符串对象,如使用关键字的那些最后,虽然接受一个棘手的语义问题,如果我在面试中得到它会惹恼我

答案 3 :(得分:0)

字符串池(字符串常量/内存/文字池)与常量池

当编译器遇到任何字符串文字时,编译器会将其放入String Constant Pool中。所有方法或类变量都引用该字符串常量池;

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框架中。

enter image description here

  • 字符串文字始终引用类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."<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;
}

字节码与上面的字节码非常相似。但是,您会看到更多putfieldgetfield的条目,这些条目告诉我们将常量放入CP中。

让我们比较上面代码的字节代码;

使用Final的'

  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;

没有决赛'

  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;

使用Final的'

  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."<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;   

此比较还证明在比较时替换最终变量的值。并且常量字段保存到CP中。因此,如果's'不是最终的那么's的值是使用getfield从CP中获取的。