Java 中的字符串是如何创建和存储的?

How are Strings created and stored in Java?

为了了解 String 对象是如何创建和存储的,我尝试了以下程序,我看到了我要查询的输出。有人可以帮忙吗?

package corejava.immutable;

public class StringTester {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String s1 = "Omkar Patkar";
        String s2 = "Omkar Patkar";
        String s3 = "Omkar" + " Patkar";
        String s4 = "Omkar";
        String s5 = s4 +" Patkar";
        String s6 = new String("Omkar Patkar");

        System.out.println("Hashcode s1 = "+s1.hashCode());
        System.out.println("Hashcode s2 = "+s2.hashCode());
        System.out.println("Hashcode s3 = "+s3.hashCode());
        System.out.println("Hashcode s4 = "+s4.hashCode());
        System.out.println("Hashcode s5 = "+s5.hashCode());
        System.out.println("Hashcode s6 = "+s6.hashCode());

        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        System.out.println("Indentity Hashcode s1 = "+System.identityHashCode(s1));
        System.out.println("Indentity Hashcode s2 = "+System.identityHashCode(s2));
        System.out.println("Indentity Hashcode s3 = "+System.identityHashCode(s3));
        System.out.println("Indentity Hashcode s4 = "+System.identityHashCode(s4));
        System.out.println("Indentity Hashcode s5 = "+System.identityHashCode(s5));
        System.out.println("Indentity Hashcode s6 = "+System.identityHashCode(s6));
        System.out.println("Indentity Hashcode intern s6 = "+System.identityHashCode(s6.intern()));

        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        System.out.println("s1 == s2 : - "+(s1 == s2));
        System.out.println("s1 == s3 : - "+(s1 == s3));
        System.out.println("s1 == s4 : - "+(s1 == s4));
        System.out.println("s1 == s5 : - "+(s1 == s5));
        System.out.println("s1 == s6 : - "+(s1 == s6));

        System.out.println("\ns2 == s3 : - "+(s2 == s3));
        System.out.println("s2 == s4 : - "+(s2 == s4));
        System.out.println("s2 == s5 : - "+(s2 == s5));
        System.out.println("s2 == s6 : - "+(s2 == s6));

        System.out.println("\ns3 == s4 : - "+(s3 == s4));
        System.out.println("s3 == s5 : - "+(s3 == s5));
        System.out.println("s3 == s6 : - "+(s3 == s6));

        System.out.println("\ns4 == s5 : - "+(s4 == s5));
        System.out.println("s4 == s6 : - "+(s4 == s6));

        System.out.println("\ns5 == s6 : - "+(s5 == s6));

        System.out.println("\ns1 == s6.intern() : - "+(s1 == s6.intern()));
        System.out.println("s2 == s6.intern() : - "+(s2 == s6.intern()));
        System.out.println("s3 == s6.intern() : - "+(s3 == s6.intern()));
        System.out.println("s4 == s6.intern() : - "+(s4 == s6.intern()));
        System.out.println("s5 == s6.intern() : - "+(s5 == s6.intern()));


    }

}

我看到了以下输出:-

Hashcode s1 = 2062602683
Hashcode s2 = 2062602683
Hashcode s3 = 2062602683
Hashcode s4 = 76311326
Hashcode s5 = 2062602683
Hashcode s6 = 2062602683
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Indentity Hashcode s1 = 4337374
Indentity Hashcode s2 = 4337374
Indentity Hashcode s3 = 4337374
Indentity Hashcode s4 = 18019860
Indentity Hashcode s5 = 31054905
Indentity Hashcode s6 = 605645
Indentity Hashcode intern s6 = 4337374
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
s1 == s2 : - true
s1 == s3 : - true
s1 == s4 : - false
s1 == s5 : - false
s1 == s6 : - false

s2 == s3 : - true
s2 == s4 : - false
s2 == s5 : - false
s2 == s6 : - false

s3 == s4 : - false
s3 == s5 : - false
s3 == s6 : - false

s4 == s5 : - false
s4 == s6 : - false

s5 == s6 : - false

s1 == s6.intern() : - true
s2 == s6.intern() : - true
s3 == s6.intern() : - true
s4 == s6.intern() : - false
s5 == s6.intern() : - false

s5 和 s6 的身份哈希码与 s1、s2、s3 不同...为什么会这样?

这些对象是在哪个内存区域创建的? ...对象图将有助于理解...

您已经创建了字符串文字和字符串对象。像 s1 和 s2 这样的字符串文字存储在字符串池中。因为它们是相同的字符串,所以它们具有相同的引用。这是有效的。

使用 new 关键字创建的字符串对象会生成一个存储在堆上的对象。它们的处理方式与任何其他对象相同。创建 2 个具有相同 String 值的对象将导致 2 个对象,每个对象都有自己的引用。使用 new 的效率与 String 池中的 String 文字不同。驻留 String 对象会将其移动到 String 池中。

当您使用 == 比较 2 个对象时,您是在比较它们的引用。因此,比较具有相同值的 2 个字符串文字将导致测试为真。但是,测试使用 new 关键字创建的 2 个对象不会。这就是为什么你应该使用 equals 方法来比较对象。

编辑

由 2 个字符串文字串联创建的字符串将产生一个字符串文字,例如 s3。因此,s3 具有与 s1 和 s2 相同的身份哈希码。但是,s5 是从字符串文字和对字符串文字的 reference 创建的,从而产生了一个新对象。这解释了为什么它有不同的身份哈希码。

我进行了更多搜索,发现了来自 javaranch 的 link,它解决了围绕创建 String 对象的普遍困惑。我还修改了我的程序以验证文章中的声明。这是 link:-

http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html

按此进行:-

  1. 所有对象总是在堆上创建。字符串对象没有 例外。
  2. 字符串池或table更像是一个查找,检查对象是否需要 为文字常量(不是新的)创建...如果是,则创建 它在堆上并记下它在池中的引用或 table.
  3. 如果具有相同值的字符串对象已经存在并且引用了 它存在于 table 或 String 池中...使用相同的引用 (在我们的示例中为 s1、s2、s3)
  4. 如果要用new创建String对象,不用查找,直接 去创建一个新对象并使用这个新引用
  5. 如果有人在此对象上调用 "intern"(根据 (4) 创建)... 然后在匹配字符串的 table...和 ​​return 引用中查找 文字。
  6. 如果使局部变量引用空值,字符串字面量 常量仍继续由 table 或字符串池引用, 因此从来没有垃圾收集。

但是 没有特殊区域 调用了 STRING POOL,其中创建了字符串文字常量。这就是我从 link 中得到的。如有错误请指正

我通过在程序中添加以下行来验证第 6. 点:-

s1 = s2 = s3 = s5 = s6 = null;
System.gc();
try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
System.gc();
System.out.println("\n\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
System.out.println("String literal pool's available or not ?");
System.out.println("Omkar Patkar".hashCode());
System.out.println(System.identityHashCode("Omkar Patkar"));

他们给出了相同的哈希码和身份哈希码。

Hashcode s1 = 2062602683
Hashcode s2 = 2062602683
Hashcode s3 = 2062602683
Hashcode s4 = 76311326
Hashcode s5 = 2062602683
Hashcode s5.intern() = 2062602683
Hashcode s6 = 2062602683
Hashcode s6.intern() = 2062602683
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Indentity Hashcode s1 = 20732290
Indentity Hashcode s2 = 20732290
Indentity Hashcode s3 = 20732290
Indentity Hashcode s4 = 18378667
Indentity Hashcode s5 = 21722195
Indentity Hashcode s6 = 12719253
Indentity Hashcode intern s6 = 20732290
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

String literal pool's available or not ?
2062602683
20732290