下面这两种说法有什么不同?

String s = "text";

String s = new String("text");

当前回答

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

String s = "text";

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

String s = new String("text");

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

另一方面,字符串字面值被重用。如果你在你的类的多个地方使用“text”,它实际上将是一个且只有一个String(即在池中多次引用同一个字符串)。

其他回答

当你使用一些东西,如new String(“Hello World”),SpotBugs代码分析工具,抱怨一个性能问题。

这个问题的描述显示了新字符串和常量字符串的区别

使用java.lang.String(String)构造函数会浪费内存,因为 这样构造的对象将在功能上难以区分 作为参数传递的字符串。只需使用参数String 直接。 错误类型和模式:Dm - DM_STRING_CTOR

当您将字符串存储为

String string1 = "Hello";

然后JVM在称为String常量池的单独内存块中创建一个具有给定价格的String对象。

当我们有尝试生成另一个String的倾向时

String string2 = "Hello";

JVM验证String常量池中是否存在价格恒定的String对象,如果存在,JVM不会创建一个全新的对象,而是将现有对象的引用赋值给新变量。

当我们存储String为

String string = new String("Hello");

使用new关键字,不管String常量池的内容是什么,都会生成一个具有给定价格的全新对象。

字符串字面值将进入字符串常量池。

下面的快照可能会帮助你从视觉上理解它,从而更长时间地记住它。


逐行创建对象:

String str1 = new String("java5");

在构造函数中使用字符串字面值“java5”,新的字符串值存储在字符串常量池中。 使用new操作符,在堆中创建一个以“java5”为值的新字符串对象。

String str2 = "java5"

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

String str3 = new String(str2);

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

String str4 = "java5";

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

对象总数:堆- 2,池- 1

Oracle社区的进一步阅读

一个简单的理解差异的方法如下:-

String s ="abc";
String s1= "abc";
String s2=new String("abc");

        if(s==s1){
            System.out.println("s==s1 is true");
        }else{
            System.out.println("s==s1 is false");
        }
        if(s==s2){
            System.out.println("s==s2 is true");
        }else{
            System.out.println("s==s2 is false");
        }

输出是

s==s1 is true
s==s2 is false

因此new String()总是会创建一个新实例。

JLS

这个概念被JLS称为“实习”。

来自JLS 7 3.10.5的相关段落:

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern. Example 3.10.5-1. String Literals The program consisting of the compilation unit (§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"; } and the compilation unit: package other; public class Other { public static String hello = "Hello"; } produces the output: true true true true false true

JVMS

JVMS 7 5.1说:

A string literal is a reference to an instance of class String, and is derived from a CONSTANT_String_info structure (§4.4.3) in the binary representation of a class or interface. The CONSTANT_String_info structure gives the sequence of Unicode code points constituting the string literal. The Java programming language requires that identical string literals (that is, literals that contain the same sequence of code points) must refer to the same instance of class String (JLS §3.10.5). In addition, if the method String.intern is called on any string, the result is a reference to the same class instance that would be returned if that string appeared as a literal. Thus, the following expression must have the value true: ("a" + "b" + "c").intern() == "abc" To derive a string literal, the Java Virtual Machine examines the sequence of code points given by the CONSTANT_String_info structure. If the method String.intern has previously been called on an instance of class String containing a sequence of Unicode code points identical to that given by the CONSTANT_String_info structure, then the result of string literal derivation is a reference to that same instance of class String. Otherwise, a new instance of class String is created containing the sequence of Unicode code points given by the CONSTANT_String_info structure; a reference to that class instance is the result of string literal derivation. Finally, the intern method of the new String instance is invoked.

字节码

看看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);
    }
}

我们有常数池

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

和主要:

 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

注意:

0和3:加载相同的LDC #2常量(字面量) 12:创建一个新的字符串实例(使用#2作为参数) 35: a和c作为常规对象与if_acmpne进行比较

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

它有一个专用的CONSTANT_String_info结构,不像常规对象(例如new String) 该结构指向包含数据的CONSTANT_Utf8_info结构体。这是表示字符串的唯一必要数据。

上面的jvm引用似乎是说,只要Utf8指向的是相同的,那么ldc就加载相同的实例。

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

String s = "abc"通过ConstantValue Attribute指向常量表 非final字段没有这个属性,但仍然可以用LDC初始化

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

额外的好处:将其与Integer池进行比较,后者没有直接的字节码支持(即没有CONSTANT_String_info模拟)。