我现在正在学习字符串常量池,基于jdk1.8。
我已经学习了相关的知识,但是当我测试这些项目时,结果让我有些困惑。
我读过一些博客,但没有得到详细原因。
// Test1
String sa = new String("lmn");
String sa1 = new String("opq");
StringBuilder stringBuilder = new StringBuilder().append(sa).append(sa1);
String sa2 = stringBuilder.toString();
String sa3 = sa2.intern();
String sa4 = "lmnopq";
System.out.println("sa2 == sa3 ? " + (sa2 == sa3)); // true why?
System.out.println("sa3 == sa4 ? " + (sa3 == sa4)); // true
// Test2
String sss = new String("ghi") + new String("hjk");
String sss1 = sss.intern();
System.out.println("sss == sss1 ? " + (sss == sss1)); // true why?
// Test3
String sc = new String("rszt") + new String("zuvw");
String sc1 = "rsztzuvw";
String sc2 = sc.intern();
System.out.println("sc == sc1 ? " + (sc == sc1)); // false why?
我有三个问题:
sa2 == sa3
? lmnopq
什么时候存储在字符串常量池中?new String("ghi") + new String("hjk")
被优化为使用stringBuilder.append()
,所以sss == sss1
的原因和sa2 == sa3
一样?String sc1 = "rsztzuvw";
之前添加了String sc2 = sc.intern();
,那为什么现在是sc != sc1
呢?为什么 sa2 == sa3 ?
我们假设
sa2
不在字符串池中。
在Java 7及更高版本中,
sa2.intern()
将使sa2
对象成为字符串池的一部分,并将其作为结果返回。在这种情况下,sa1
和sa3
将引用同一个对象。
在 Java 7 之前,
sa2.intern()
会在 PermGen 堆中创建一个 new 字符串,使其成为字符串池的一部分并返回它。在这种情况下,sa1
和sa3
将指代不同的对象。
lmnopq什么时候存储在字符串常量池中?
这取决于两件事:
intern()
的行为;见上文。String sa4 = "lmnopq";
中的字符串文字是否在类加载或初始化时或首次使用字符串文字时被保留。这也取决于 Java 的版本。我查看了字节码文件,发现
被优化为使用new String("ghi") + new String("hjk")
,所以stringBuilder.append()
的原因和sss == sss1
一样?sa2 == sa3
不,这不是原因。
顺便说一句,最新版本的 Javac(我认为是 Java 8 之后)无论如何都不会进行这种优化。
Test2 和 Test3 类似,不同的是我添加了 String sc1 = "rsztzuvw";之前是 String sc2 = sc.intern();,那为什么现在是 sc != sc1 呢?
这与我上面提到的关于 when 字符串文字被保留的内容有关。显然,您使用的 JVM 是在第一次使用字符串文字时进行实习的。移动使用该文字的行意味着它在显式 intern()
调用之前
被保留。(很复杂,仔细想想。)
我的建议:
这是无用的知识。作为程序员,它不会帮助你准确地了解每个版本的 Java 如何实现字符串池、通过 intern()
调用显式驻留以及字符串文字的隐式驻留。您真正需要知道的是三个黄金法则。
equals
方法比较字符串。使用
==
通常会带来意想不到的结果。
String.intern()
。没有必要。如果确实需要节省重复字符串对象占用的空间,请确保启用 GC 字符串去重功能。
new String
参数上调用
String
。除非 JIT 编译器可以优化掉
new
,否则这是不必要且低效的。