什么是Java中的string池?

我对Java中的StringPool感到困惑。 我在阅读Java中的String章节时遇到了这个问题。 用外行的话来说,请帮助我理解StringPool实际上做了什么。

这打印true (即使我们不使用equals方法:比较string的正确方法)

  String s = "a" + "bc"; String t = "ab" + "c"; System.out.println(s == t); 

当编译器优化你的string文字时,它看到st都有相同的值,因此你只需要一个string对象。 这是安全的,因为String在Java中是不可变的。
结果, st指向相同的对象,并保存了一些小的内存。

名称“string池”来自于这样的想法:所有已经定义的string都存储在某个“池”中,并且在创build新的String对象编译器检查之前,如果已经定义了这样的string。

我不认为它实际上做了太多,它看起来只是string文字的caching。 如果有多个string的值相同,则它们将全部指向string池中相同的string字面值。

 String s1 = "Arul"; //case 1 String s2 = "Arul"; //case 2 

在情况1中,文字s1被新创build并保存在池中。 但在情况2中,文字s2指的是s1,它不会创build新的。

 if(s1 == s2) System.out.println("equal"); //Prints equal. String n1 = new String("Arul"); String n2 = new String("Arul"); if(n1 == n2) System.out.println("equal"); //No output. 

http://p2p.wrox.com/java-espanol/29312-string-pooling.html

让我们从虚拟机规范的引用开始:

加载包含String文字的类或接口可能会创build一个新的String对象(§2.4.8)来表示该文字。 如果已经创build了一个String对象来表示该文本的先前出现,或者如果String.intern方法已经在表示与文字相同的string的String对象上被调用,则可能不会发生这种情况。

这可能不会发生 – 这是一个暗示,关于String对象有一些特别的东西。 通常,调用构造函数将始终创build该类的新实例。 string不是这种情况,尤其是当string对象是用文字“创build”的时候。 这些string存储在全局存储(池)中 – 或者至less引用保存在一个池中,并且每当需要已知string的新实例时,vm将从池中返回对该对象的引用。 在伪代码中,它可能是这样的:

 1: a := "one" --> if(pool[hash("one")] == null) // true pool[hash("one") --> "one"] return pool[hash("one")] 2: b := "one" --> if(pool[hash("one")] == null) // false, "one" already in pool pool[hash("one") --> "one"] return pool[hash("one")] 

所以在这种情况下,variablesab保持对同一个对象的引用。 在这种情况下,我们有(a == b) && (a.equals(b)) == true

如果我们使用构造函数,情况并非如此:

 1: a := "one" 2: b := new String("one") 

再次,在池上创build"one" ,但是我们从相同的文字创build一个新的实例,在这种情况下,它导致(a == b) && (a.equals(b)) == false

那么为什么我们有一个string池? string,特别是string文字在典型的Java代码中被广泛使用。 而且他们是不变的。 不可变的允许cachingstring来节省内存并提高性能(减less创build工作量,减less垃圾收集)。

作为程序员,我们不必太在乎string池,只要我们记住:

  • (a == b) && (a.equals(b))可能是truefalse总是使用equals来比较string)
  • 不要使用reflection来改变string的支持char[] (因为你不知道谁正在使用该string执行)

当JVM加载类,或以其他方式看到一个文字string,或者一些代码intern是一个string时,它将string添加到每个这样的string有一个副本的大部分隐藏的查找表中。 如果添加了另一个副本,则运行时会对其进行排列,以便所有文字都引用相同的string对象。 这被称为“实习”。 如果你这样说

 String s = "test"; return (s == "test"); 

它会返回true ,因为第一个和第二个“testing”实际上是同一个对象。 以这种方式比较string可以比String.equals快很多,因为只有一个引用比较而不是一堆char比较。

你可以通过调用String.intern()来添加一个string到池中,这会返回string的合并版本(可能是你正在实习的同一个string,但是你会疯狂的依靠这个string – – 你经常无法确定哪些代码已经被加载,直到现在运行,并实现相同的string)。 混合版本(从intern返回的string)将等于任何相同的文字。 例如:

 String s1 = "test"; String s2 = new String("test"); // "new String" guarantees a different object System.out.println(s1 == s2); // should print "false" s2 = s2.intern(); System.out.println(s1 == s2); // should print "true"