Java字符串常量池困惑

问题描述 投票:0回答:1

我现在正在学习字符串常量池,基于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?

我有三个问题:

  1. 为什么
    sa2 == sa3
    lmnopq
    什么时候存储在字符串常量池中?
  2. 我查看了字节码文件,发现
    new String("ghi") + new String("hjk")
    被优化为使用
    stringBuilder.append()
    ,所以
    sss == sss1
    的原因和
    sa2 == sa3
    一样?
  3. Test2Test3类似,不同的是我在
    String sc1 = "rsztzuvw";
    之前添加了
    String sc2 = sc.intern();
    ,那为什么现在是
    sc != sc1
    呢?
java string-constant
1个回答
0
投票

为什么 sa2 == sa3 ?

我们假设

sa2
不在字符串池中。

  • 在Java 7及更高版本中,

    sa2.intern()
    将使
    sa2
    对象成为字符串池的一部分,并将其作为结果返回。在这种情况下,
    sa1
    sa3
    将引用同一个对象。

  • 在 Java 7 之前,

    sa2.intern()
    会在 PermGen 堆中创建一个 new 字符串,使其成为字符串池的一部分并返回它。在这种情况下,
    sa1
    sa3
    将指代不同的对象。

lmnopq什么时候存储在字符串常量池中?

这取决于两件事:

  1. intern()
    的行为;见上文。
  2. 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()

 调用显式驻留以及字符串文字的隐式驻留。

您真正需要知道的是三个黄金法则。

    始终使用
  1. equals
     方法比较字符串。使用 
    ==
     通常会带来意想不到的结果。
  2. 不要明确调用
  3. String.intern()
    。没有必要。如果确实需要节省重复字符串对象占用的空间,请确保启用 GC 字符串去重功能。
  4. 不要在
  5. new String
     参数上调用 
    String
    。除非 JIT 编译器可以优化掉 
    new
    ,否则这是不必要且低效的。
如果你遵守这些规则,实习中的棘手细节就无关紧要了。 “因为无论这些细节如何,遵循规则的代码都会给出正确的答案。

© www.soinside.com 2019 - 2024. All rights reserved.