下面这两种说法有什么不同?
String s = "text";
String s = new String("text");
下面这两种说法有什么不同?
String s = "text";
String s = new String("text");
当前回答
字符串字面值将进入字符串常量池。
下面的快照可能会帮助你从视觉上理解它,从而更长时间地记住它。
逐行创建对象:
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社区的进一步阅读
其他回答
把“bla”想象成一个魔法工厂,就像Strings.createString(“bla”)(pseudo)。工厂保存了所有以这种方式创建的字符串池。
如果它被调用,它会检查池中是否已经有这个值的字符串。如果为真,则返回此string对象,因此以这种方式获得的字符串确实是同一个对象。
如果不是,它在内部创建一个新的字符串对象,将其保存在池中,然后返回它。因此,当下次查询相同的字符串值时,它将返回相同的实例。
手动创建新的String("")通过绕过字符串字面量池来覆盖这种行为。因此,应该始终使用equals()检查相等性,它比较字符序列,而不是对象引用相等性。
字符串字面值将进入字符串常量池。
下面的快照可能会帮助你从视觉上理解它,从而更长时间地记住它。
逐行创建对象:
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 obj1 = "abc";
String obj2 = "abc";
"obj1"和"obj2"将指向相同的字符串字面量,字符串字面量池将只有一个"abc"字面量。
当我们使用new关键字创建String类对象时,这样创建的字符串存储在堆内存中。任何作为参数传递给string类的构造函数的字符串字面值都存储在字符串池中。如果我们使用new操作符使用相同的值创建多个对象,每次都会在堆中创建一个新对象,因为应该避免使用这个new操作符。
String obj1 = new String("abc");
String obj2 = new String("abc");
"obj1"和"obj2"将指向堆中两个不同的对象,字符串字面量池将只有一个"abc"字面量。
关于字符串的行为还有一点值得注意,那就是任何对字符串进行的赋值或连接都会在内存中创建一个新对象。
String str1 = "abc";
String str2 = "abc" + "def";
str1 = "xyz";
str2 = str1 + "ghi";
在上面的例子中: 第1行:“abc”字面值存储在字符串池中。 第2行:"abcdef"字面值存储在字符串池中。 第3行:一个新的“xyz”字面值存储在字符串池中,“str1”开始指向这个字面值。 第4行:由于该值是通过附加到另一个变量而生成的,因此结果存储在堆内存中,被附加的字面量“ghi”将在字符串池中检查是否存在,并将被创建,因为在上述情况下它不存在。
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模拟)。
一个简单的理解差异的方法如下:-
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()总是会创建一个新实例。