什么是Java中的string池?
我对Java中的StringPool感到困惑。 我在阅读Java中的String章节时遇到了这个问题。 用外行的话来说,请帮助我理解StringPool实际上做了什么。
这打印true
(即使我们不使用equals
方法:比较string的正确方法)
String s = "a" + "bc"; String t = "ab" + "c"; System.out.println(s == t);
当编译器优化你的string文字时,它看到s
和t
都有相同的值,因此你只需要一个string对象。 这是安全的,因为String
在Java中是不可变的。
结果, s
和t
指向相同的对象,并保存了一些小的内存。
名称“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.
让我们从虚拟机规范的引用开始:
加载包含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")]
所以在这种情况下,variablesa
和b
保持对同一个对象的引用。 在这种情况下,我们有(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))
可能是true
或false
( 总是使用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"