"text"和new String("text")之间有什么区别?

use*_*141 187 java string

这两个陈述之间有什么区别?

String s = "text";

String s = new String("text");
Run Code Online (Sandbox Code Playgroud)

pol*_*nts 180

new String("text"); 显式创建一个新的和引用不同的String对象实例; String s = "text";可以重用字符串常量池中的实例(如果有).

很少想要使用new String(anotherString)构造函数.来自API:

String(String original):初始化新创建的 String对象,使其表示与参数相同的字符序列; 换句话说,新创建的字符串是参数字符串的副本.除非需要显式的原始副本,否则不需要使用此构造函数,因为字符串是不可变的.

相关问题


什么参考区别意味着什么

检查以下代码段:

    String s1 = "foobar";
    String s2 = "foobar";

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

    s2 = new String("foobar");
    System.out.println(s1 == s2);      // false
    System.out.println(s1.equals(s2)); // true
Run Code Online (Sandbox Code Playgroud)

==两种引用类型是引用标识比较.两个equals不一定的对象==.==在引用类型上使用通常是错误的; 大部分时间都equals需要使用.

尽管如此,如果出于某种原因需要创建两个equals但不是==字符串,则可以使用new String(anotherString)构造函数.然而,需要再说一遍,这是非常奇特的,很少是意图.

参考

相关问题

  • 如果我写:String s = new String("abc"); 现在我写道:String s ="abc"; 将String s ="abc"; 在String池中创建一个新的String文字? (3认同)
  • @KaveeshKanwal不,文字不会重复.如你所见,有2个"abc".其中只有一个将转到String池,另一个将引用它.然后是`s`,这将是适当的新对象. (2认同)

Bra*_*raj 112

字符串文字将进入字符串常量池.

下面的快照可能会帮助您直观地理解它以便记住它更长的时间.

在此输入图像描述


逐行创建对象:

String str1 = new String("java5");
Run Code Online (Sandbox Code Playgroud)

在构造函数中使用字符串文字"java5",新的字符串值存储在字符串常量池中.使用new运算符,在堆中创建一个新的字符串对象,并将"java5"作为值.

String str2 = "java5"
Run Code Online (Sandbox Code Playgroud)

引用"str2"指向字符串常量池中已存储的值

String str3 = new String(str2);
Run Code Online (Sandbox Code Playgroud)

在堆中创建一个新的字符串对象,其值与"str2"引用的值相同

String str4 = "java5";
Run Code Online (Sandbox Code Playgroud)

引用"str4"指向字符串常量池中已存储的值

总对象:堆 - 2,池 - 1

进一步阅读Oracle社区

  • 不正确。常量池是在编译时创建的,而不是在执行时创建的。不要对未引用的文本使用引号格式。 (3认同)
  • 是的,我检查过它不会改变str4的值 (2认同)

小智 15

一个在字符串常量池中创建一个String

String s = "text";
Run Code Online (Sandbox Code Playgroud)

另一个在常量池("text")中创建一个字符串,在普通堆空间(s)中创建另一个字符串.两个字符串都具有相同的值,即"text".

String s = new String("text");
Run Code Online (Sandbox Code Playgroud)

s 如果以后未使用,则会丢失(符合GC条件).

另一方面,字符串文字被重用.如果您"text"在类的多个位置使用它实际上将是一个且只有一个String(即对池中相同字符串的多个引用).


Cir*_*四事件 8

JLS

这个概念被JLS称为"实习".

JLS 7的相关段落3.10.5:

此外,字符串文字始终引用类String的相同实例.这是因为字符串文字 - 或者更常见的是作为常量表达式(第15.28节)的值的字符串 - 被"实例化"以便使用String.intern方法共享唯一实例.

例3.10.5-1.字符串文字

该程序由编译单元组成(第7.3节):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }
Run Code Online (Sandbox Code Playgroud)

和编译单位:

package other;
public class Other { public static String hello = "Hello"; }
Run Code Online (Sandbox Code Playgroud)

产生输出:

true true true true false true
Run Code Online (Sandbox Code Playgroud)

JVMS

JVMS 7 5.1说:

字符串文字是对类String实例的引用,它是从类或接口的二进制表示形式的CONSTANT_String_info结构(第4.4.3节)派生而来的.CONSTANT_String_info结构给出了构成字符串文字的Unicode代码点序列.

Java编程语言要求相同的字符串文字(即包含相同代码点序列的文字)必须引用类String的相同实例(JLS§3.10.5).此外,如果在任何字符串上调用String.intern方法,则结果是对该字符串显示为文字时将返回的同一类实例的引用.因此,以下表达式的值必须为true:

("a" + "b" + "c").intern() == "abc"
Run Code Online (Sandbox Code Playgroud)

为了派生字符串文字,Java虚拟机检查CONSTANT_String_info结构给出的代码点序列.

  • 如果先前在类String的实例上调用了String.intern方法,该类包含与CONSTANT_String_info结构给出的Unicode代码点序列相同的Unicode代码点序列,则字符串文字派生的结果是对类String的同一实例的引用.

  • 否则,将创建一个类String的新实例,其中包含CONSTANT_String_info结构给出的Unicode代码点序列; 对该类实例的引用是字符串文字派生的结果.最后,调用新String实例的intern方法.

字节码

查看OpenJDK 7上的字节码实现也很有启发性.

如果我们反编译:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们有恒定的池:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc
Run Code Online (Sandbox Code Playgroud)

并且main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V
Run Code Online (Sandbox Code Playgroud)

请注意:

  • 03:ldc #2加载相同的常量(文字)
  • 12:创建一个新的字符串实例(使用#2as参数)
  • 35:ac作为常规对象进行比较if_acmpne

常量字符串的表示在字节码上非常神奇:

并且上面的JVMS引用似乎表明只要Utf8指向的是相同的,就会加载相同的实例ldc.

我已经对字段进行了类似的测试,并且:

  • static final String s = "abc"通过ConstantValue属性指向常量表
  • 非final字段没有该属性,但仍可以使用 ldc

结论:对字符串池有直接的字节码支持,并且内存表示是有效的.

额外:将其与整数池进行比较,整数池没有直接的字节码支持(即没有CONSTANT_String_info模拟).