简体   繁体   English

字符串生成器不使用字符串池中的字符串文字。 为什么?

[英]String builder not using string literal from string pool. Why?

String s1 = "Java";
String s2 = "Java";
StringBuilder sb1 = new StringBuilder();

sb1.append("Java");

System.out.println(s1 == s2);                  //true
System.out.println(s1.equals(s2));             //true
System.out.println(sb1.toString() == s1);      //false
System.out.println(sb1.toString().equals(s1)); //true

Why is the third print statement printing false?为什么第三个打印语句打印错误? How to make it print true?如何使它打印真实?

Because the StringBuilder uses it's own data structure.因为 StringBuilder 使用它自己的数据结构。 It does not store the reference to the string, it copies it.它不存储对字符串的引用,而是复制它。 There is no way to make it print true.没有办法让它打印为真。

The implementation (at least for Java8 ) for StringBuilder.toString() is below: StringBuilder.toString()的实现(至少对于Java8 )如下:

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

Here you can see new String(char value[], int offset, int count) is called so == will be false .在这里你可以看到new String(char value[], int offset, int count)被调用,所以==将是false

You can test the equivalent code manually below:您可以在下面手动测试等效代码:

String s1 = "Java";
String s2 = "Java";
System.out.println(s1 == s2); //true
System.out.println(new String(s1) == s2); //false
System.out.println(new String(s1.toCharArray(), 0, 4) == s2); //false

Here you can see new String() creates a new Object so == is false.在这里你可以看到new String()创建了一个新对象,所以==是假的。

This can also be seen in the documentation for new String :这也可以在new String的文档中看到:

Initializes a newly created {@code String} object so that it represents the same sequence of characters as the argument;初始化一个新创建的 {@code String} 对象,使其表示与参数相同的字符序列; in other words, the newly created string is a copy of the argument string.换句话说,新创建的字符串是参数字符串的副本。 Unless an explicit copy of {@code original} is needed, use of this constructor is unnecessary since Strings are immutable.除非需要 {@code original} 的显式副本,否则不需要使用此构造函数,因为字符串是不可变的。

Note: Java9+ uses byte [] instead of char [] behind the scenes for String , so some of the specifics may have changed, but the idea stays the same.注意: Java9+String的幕后使用byte []而不是char [] ,所以一些细节可能已经改变,但想法保持不变。

1. String Interning 1. 字符串实习

As an result of Strings immutability the JVM can make optimization to store only one copy of each literal String in the Java String Pool (the special memory region where Strings are stored by the JVM).由于字符串的不变性,JVM 可以进行优化,以便在 Java 字符串池(JVM 存储字符串的特殊内存区域)中仅存储每个文字字符串的一个副本。 This process is called interning这个过程叫做实习

If found, the Java compiler will simply return a reference to its memory address, without allocating additional memory.如果找到,Java 编译器将简单地返回对其内存地址的引用,而不分配额外的内存。 If not found, it'll be added to the pool and its reference will be returned.如果未找到,它将被添加到池中并返回其引用。

String s1 = "Java";
String s2 = "Java";

System.out.println(s1 == s2); //true

2. Strings Allocated Using the Constructor 2. 使用构造函数分配的字符串

Creating a string using the new operator will create a new object and store it in the heap space使用new运算符创建字符串将创建一个新对象并将其存储在堆空间中

Every String created like this will point to a different memory region with its own address像这样创建的每个字符串都将指向具有自己地址的不同内存区域

String s1 = "Java";
String s2 = new String("Java"); //or
StringBuilder sb1 = new StringBuilder();

sb1.append("Java");

System.out.println(sb1.toString() == s1);; //false
System.out.println(s2 == s1);; //false

3. Conclusion 3. 结论

When we create a String object using the new() operator, it always creates a new object in heap memory.当我们使用 new() 操作符创建一个 String 对象时,它总是在堆内存中创建一个新对象。 On the other hand, if we create an object using String literal, it may return an existing object from the String pool另一方面,如果我们使用字符串字面量创建一个对象,它可能会从字符串池中返回一个现有对象

4. Manual Interning 4. 人工实习

Manually interning the String will store its reference in the pool, and the JVM will return this reference when needed手动实习 String 会将其引用存储在池中,JVM 将在需要时返回此引用

String s1 = "Java";
String s2 = new String("Java").intern(); //or
StringBuilder sb1 = new StringBuilder();

sb1.append("Java");

System.out.println(sb1.toString().intern() == s1);; //true
System.out.println(s2 == s1);; //true

I have added a couple of more lines to your code to make it clear for you.我在您的代码中添加了几行,以使您清楚。 Feel free to comment in case of any doubt.如有任何疑问,请随时发表评论。

public class Main {
    public static void main(String[] args) {
        String s1 = "Java";
        String s2 = "Java";
        StringBuilder sb1 = new StringBuilder();
        sb1.append("Java");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(sb1.toString() == s1);
        System.out.println(sb1.toString().equals(s1));

        String s3 = new String("Java"); // It's a new String similar to what sb1.toString() returns
        System.out.println(s1 == s3);// false
    }
}

Output:输出:

true
true
false
true
false

Do check this to explore how StringBuilder::toString has been implemented.请检查这个以探索StringBuilder::toString是如何实现的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM