2

我对这两件事感到困惑。我需要帮助。请清除我的疑问,字符串常量池和字符串池是否是同一个概念。我在面试时遇到了这个问题。我已经阅读了很多网站和博客,但是我的疑问没有被清除。请清除我的疑问。

提前致谢。

4

4 回答 4

5

两者都是一回事。字符串常量池包含常量字符串对象。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 。

于 2013-05-28T04:56:51.517 回答
3

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

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

于 2013-05-28T05:51:56.010 回答
2

我想了想,但我不确定,但字符串池可能指的是字符串文字池,比如String apple = "apple";字符串常量池可能指的是常量字符串对象,比如使用关键字 final 的对象,尽管收到一个问题如果我在面试中遇到这种棘手的语义,我会很生气

于 2013-05-28T04:51:48.687 回答
2

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

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

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”将保存在字符串池中。

"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 获取的。

于 2016-09-25T13:24:35.423 回答