以下两个陈述之间有什么区别?
String s = "text";
String s = new String("text");
答案 0 :(得分:175)
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
两个引用类型上的 ==
是引用标识比较。 equals
的两个对象不一定是==
。在引用类型上使用==
通常是错误的;大部分时间都需要使用equals
。
尽管如此,如果出于某种原因需要创建两个equals
但不是==
字符串,可以使用new String(anotherString)
构造函数。然而,需要再说一遍,这是非常特有的,很少是意图。
答案 1 :(得分:106)
字符串literals将进入字符串常量池。
以下快照可能会帮助您理解以可视方式以便长时间记住它。
逐行创建对象:
String str1 = new String("java5");
使用字符串文字" java5"在构造函数中,新的字符串值存储在字符串常量池中。 使用new运算符,在堆中使用" java5"创建一个新的字符串对象。作为价值。
String str2 = "java5"
参考" str2"指向已存储的字符串常量池
中的值String str3 = new String(str2);
在堆中创建一个新的字符串对象,其值与引用的值相同" str2"
String str4 = "java5";
参考" str4"指向已存储的字符串常量池
中的值总对象:堆 - 2,池 - 1
答案 2 :(得分:15)
String s = "text";
另一个在常量池("text"
)中创建一个字符串,在正常堆空间(s
)中创建另一个字符串。两个字符串都具有相同的值,即“text”。
String s = new String("text");
如果以后未使用, s
将丢失(符合GC条件)。
另一方面,字符串文字被重用。如果在类的多个位置使用"text"
,它实际上只有一个String(即对池中相同字符串的多个引用)。
答案 3 :(得分:8)
这个概念被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"; }
和编译单元:
package other; public class Other { public static String hello = "Hello"; }
产生输出:
true true true true false true
字符串文字是对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"
为了派生字符串文字,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);
}
}
我们在常量池上:
#2 = String #32 // abc
[...]
#32 = Utf8 abc
和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
请注意:
0
和3
:加载相同的ldc #2
常量(文字)12
:创建一个新的字符串实例(以#2
作为参数)35
:a
和c
作为常规对象与if_acmpne
进行比较常量字符串的表示在字节码上非常神奇:
new String
)并且上面的JVMS引用似乎表明,只要Utf8指向的是相同的,那么ldc
就会加载相同的实例。
我已经对字段进行了类似的测试,并且:
static final String s = "abc"
通过ConstantValue Attribute ldc
结论:对字符串池有直接的字节码支持,内存表示效率很高。
奖励:将其与Integer pool进行比较,{{3}}没有直接的字节码支持(即没有CONSTANT_String_info
类似物。)
答案 4 :(得分:1)
认为"bla"
是一个神奇的工厂,如Strings.createString("bla")
(伪)。工厂拥有一个所有字符串的池,但这样创建。
如果它被调用,它会检查池中是否已存在具有此值的字符串。如果为true,则返回此字符串对象,因此以这种方式获得的字符串确实是同一个对象。
如果没有,它会在内部创建一个新的字符串对象,将其保存在池中然后返回它。因此,当下次查询相同的字符串值时,它将返回相同的实例。
手动创建new String("")
会绕过字符串文字池来覆盖此行为。因此,应始终使用equals()
检查相等性,{{1}}比较字符序列而不是对象引用相等。
答案 5 :(得分:1)
理解差异的一种简单方法如下: -
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
因此,新的String()将始终创建一个新实例。
答案 6 :(得分:1)
@Braj:我想你已经提到了另一种方式。如果我错了,请纠正我
逐行创建对象:
String str1 = new String(“java5”)
Pool- "java5" (1 Object)
Heap - str1 => "java5" (1 Object)
String str2 =“java5”
pool- str2 => "java5" (1 Object)
heap - str1 => "java5" (1 Object)
String str3 = new String(str2)
pool- str2 => "java5" (1 Object)
heap- str1 => "java5", str3 => "java5" (2 Objects)
String str4 =“java5”
pool - str2 => str4 => "java5" (1 Object)
heap - str1 => "java5", str3 => "java5" (2 Objects)
答案 7 :(得分:1)
任何字符串文字都会在字符串文字池中创建,并且该池不允许任何重复。因此,如果使用相同的文字值初始化两个或多个字符串对象,则所有对象都将指向相同的文字。
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”之后的文字检查在字符串池中是否存在,并且由于在字符串池中不存在而将创建该文字。以上情况。
答案 8 :(得分:0)
虽然从程序员的角度来看它看起来是一样的,但它会对性能产生很大的影响。你几乎总是想要使用第一种形式。
答案 9 :(得分:0)
String str = new String("hello")
它会检查String常量池是否已包含字符串“hello”? 如果存在,那么它将不会在String常量池中添加条目。如果不存在,那么它将在String常量池中添加一个条目。
将在堆内存区域中创建一个对象,并在堆内存位置创建对象的str
个引用点。
如果您希望str
引用包含在String常量池中的点对象,则必须显式调用str.intern();
String str = "world";
它会检查String常量池是否已包含字符串“hello”? 如果存在,那么它将不会在String常量池中添加条目。如果不存在,那么它将在String常量池中添加一个条目。
在上述两种情况中,str
引用指向常量池中的字符串"world"
。
答案 10 :(得分:0)
将字符串存储为
String string1 = "Hello";
直接,然后JVM在称为String常量池的单独内存块中以给定的价格创建String对象。
每当我们有尝试尝试产生另一个String为
的趋势时,String string2 = "Hello";
JVM会验证String常量池中是否存在任何具有常量价格的String对象,如果不是这样,则JVM不会创建一个全新的对象,而是将现有对象的引用分配给新变量。
当我们将String存储为
String string = new String("Hello");
使用new关键字,无论String常量池的内容如何,都可以制造出具有给定价格的全新对象。
答案 11 :(得分:0)
很抱歉,您的答案很晚,但是非常需要。 首先,我们需要了解一些Java.lang.String类规则。
字符串文字,例如String str="java";
(我们仅使用双引号)
与String对象不同(我们使用new关键字)
例如String str=new String("java");
String is Immutable Object,即,如果值更改,则会创建一个新对象并返回给您,例如,请参见replace() and replaceAll()
函数以及更多其他内容。
这在修改中产生了许多字符串对象的问题,因此Java的创建者提出了一个名为StringPool的想法。 StringPool存储在堆区中,因为我们知道String是Char[]
(before java 9很长的读取时间)或byte
[](after java 9阅读)。
字符串文字存储在StringPool中,而字符串对象存储在通常的堆对象区域中。
如果仅在字符串操作中将完成许多对象字符串初始化JVM堆,则Java开发团队提出了intern()解决方案,该解决方案将对StringPool的内存引用移动/更改。
Another good link to understand java.lang.String better
import java.util.*;
class GFG {
public static void main(String[] args)
{
String siteName1 = "java.com";
String siteName2 = "java.com";
String siteName3 = new String("java.com");
String siteName4 = new String("java.com").intern();
System.out.println("siteName1:::"+Integer.toHexString(System.identityHashCode(siteName1)));
System.out.println("siteName2:::"+Integer.toHexString(System.identityHashCode(siteName2)));
System.out.println("siteName3 creation Of New Object Without Interned:::"+Integer.toHexString(System.identityHashCode(siteName3)));//must be Diffrent bcoz new Object In Heap Area
System.out.println("siteName4 creation Of New Object With Interned:::"+Integer.toHexString(System.identityHashCode(siteName4)));//must be same MemoryAddress of siteName1,siteName2 and Interned, bcoz Objects Points to String pool Now
System.out.println(siteName1 == siteName2); // true
System.out.println(siteName1 == siteName3); // false this tells about lietral vs String Objects
String siteName5 = siteName3.intern(); // Interning will not change Original Object but gives us a new Object
System.out.println("siteName5 Interned from siteName3:::"+Integer.toHexString(System.identityHashCode(siteName5)));//must be same MemoryAddress of siteName1,siteName2 and Interned, bcoz Objects Points to String pool Now
System.out.println(siteName1 == siteName3); // false this tells about Immutability
System.out.println(siteName1 == siteName5); // true After Intering both are same
System.out.println(siteName1 == siteName4); // true
System.out.println(siteName5 == siteName4); // true
}
}