以下两个语句之间有什么区别?
String s = "text";
String s = new String("text");
false
因为操作顺序指示+运算符排在最前面,并串联“ a == b?”。用a创建一个字符串“ a == b?Java”。然后,表达式的"a==b?Java" == b
计算结果为false。
以下两个语句之间有什么区别?
String s = "text";
String s = new String("text");
false
因为操作顺序指示+运算符排在最前面,并串联“ a == b?”。用a创建一个字符串“ a == b?Java”。然后,表达式的"a==b?Java" == b
计算结果为false。
Answers:
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)
构造函数。但是,需要再次说,这是非常特殊的,并且很少是故意的。
"abc"
s。其中只有一个将进入字符串池,而另一个将引用它。然后s
是合适的新对象。
字符串文字将进入字符串常量池。
下面的快照可以帮助您直观地理解它,以便长时间记住它。
逐行创建对象:
String str1 = new String("java5");
在构造函数中使用字符串文字“ java5”,新的字符串值存储在字符串常量池中。使用new运算符,将在堆中创建一个新字符串对象,并以“ java5”作为值。
String str2 = "java5"
引用“ str2”指向字符串常量池中已存储的值
String str3 = new String(str2);
在堆中创建一个新的字符串对象,其值与“ str2”所引用的值相同
String str4 = "java5";
引用“ str4”指向字符串常量池中已存储的值
物件总数:堆-2,水池-1
JLS将这个概念称为“实习”。
来自JLS 7 3.10.5的相关段落:
而且,字符串文字总是引用类String的相同实例。这是因为使用方法String.intern,对“字符串文字”(或更一般地说,是作为常量表达式的值的字符串(第15.28节))进行了“插入”,以便共享唯一的实例。
示例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代码点序列,则字符串文字派生的结果是对该类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属性指向常量表ldc
结论:字符串池直接支持字节码,并且内存表示效率很高。
奖励:将其与没有直接字节码支持(即无模拟)的Integer池进行比较CONSTANT_String_info
。
@Braj:我想你提到了另一种方式。如果我错了请指正我
逐行创建对象:
字符串str1 =新的String(“ java5”)
Pool- "java5" (1 Object)
Heap - str1 => "java5" (1 Object)
字符串str2 =“ java5”
pool- str2 => "java5" (1 Object)
heap - str1 => "java5" (1 Object)
字符串str3 =新字符串(str2)
pool- str2 => "java5" (1 Object)
heap- str1 => "java5", str3 => "java5" (2 Objects)
字符串str4 =“ java5”
pool - str2 => str4 => "java5" (1 Object)
heap - str1 => "java5", str3 => "java5" (2 Objects)
str1
不涉及str2
或str3
或的价值str4
。
想像"bla"
是像Strings.createString("bla")
(pseudo)这样的魔术工厂。工厂保存了所有以这种方式创建的字符串的池。
如果它被调用,它将检查池中是否已经有带有该值的字符串。如果为true,它将返回此字符串对象,因此以这种方式获得的字符串确实是同一对象。
如果不是,它将在内部创建一个新的字符串对象,将其保存在池中,然后返回它。因此,下一次查询相同的字符串值时,它将返回相同的实例。
new String("")
通过绕过字符串文字池,手动创建会覆盖此行为。因此,应始终检查是否使用相等来equals()
比较字符序列,而不是对象引用相等。
一种了解差异的简单方法如下:
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()将始终创建一个新实例。
任何字符串文字都在字符串文字池中创建,并且该池不允许重复。因此,如果使用相同的文字值初始化两个或多个字符串对象,则所有对象都将指向相同的文字。
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”之后的文字检查在字符串池中是否存在,并且由于在字符串池中不存在而将被创建。以上情况。
String str = new String("hello")
它将检查String常量池是否已经包含String“ hello”?如果存在,则不会在字符串常量池中添加条目。如果不存在,它将在字符串常量池中添加一个条目。
将在堆内存区域中创建一个对象,并且str
引用指向在堆内存位置中创建的对象。
如果要str
引用包含在字符串常量池中的点对象,则必须显式调用str.intern();
String str = "world";
它将检查String常量池是否已经包含String“ hello”?如果存在,则不会在字符串常量池中添加条目。如果不存在,它将在字符串常量池中添加一个条目。
在以上两种情况下,str
引用都指向"world"
常量池中存在的字符串。
当您将字符串存储为
String string1 = "Hello";
直接,然后JVM在称为String常量池的单独内存块中以给定的价格创建String对象。
并且每当我们倾向于尝试产生另一个String作为
String string2 = "Hello";
JVM会验证String常量池中是否存在任何具有常量价格的String对象,如果不是这样,则JVM不会创建全新的对象,而是将现有对象的引用分配给新变量。
当我们将String存储为
String string = new String("Hello");
使用new关键字,无论String常量池的内容如何,都将创建具有给定价格的全新对象。