“文本”和新的string(“文本”)有什么区别?
这两个声明有什么区别?
String s = "text"; String s = new String("text");
new String("text");
显式地创build一个新的和不同的String
对象的实例; String s = "text";
可以重用string常量池中的实例(如果有)。
你很less会想要使用new String(anotherString)
构造函数。 从API:
String(String original)
:初始化新创build的String
对象,使其表示与参数相同的字符序列; 换句话说,新创build的string是参数string的副本。 除非需要显式拷贝原始文件,否则使用这个构造函数是不必要的,因为string是不可变的。
相关问题
- Javastring:“String s = new String(”傻“);”
- string是Java中的对象,那么为什么我们不使用“新”来创build它们呢?
区别是指什么
检查以下代码片段:
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
==
两个引用types是参考标识比较。 两个equals
对象不一定是==
。 在引用types上使用==
通常是错误的; 大部分时间equals
需要使用。
尽pipe如此,如果由于某种原因需要创build两个equals
而不是==
string,则可以使用new String(anotherString)
构造函数。 需要再说的是,这是非常奇特的,很less有这个意图。
参考
- JLS 15.21.3参考平等操作符==和!=
-
class Object
–boolean Object(equals)
相关问题
- Java String.equals与==
- 如何比较Java中的string?
string文字将进入string常量池 。
下面的快照可能会帮助您直观地了解它,以便更长时间地记住它。
逐行创build对象:
String str1 = new String("java5");
在构造函数中使用string文字“java5”,一个新的string值被存储在string常量池中使用new操作符在堆中创build一个新的string对象,其值为“java5”
String str2 = "java5"
参考“str2”指向string常量池中已经存储的值
String str3 = new String(str2);
在堆中创build一个新的string对象,其值与“str2”引用的值相同
String str4 = "java5";
参考“str4”指向string常量池中已经存储的值
总物件:堆 – 2,池 – 1
进一步阅读Oracle社区
一个在string常量池中创build一个string
String s = "text";
另一个在常量池( "text"
)中创build一个string,在正常的堆空间中创build另一个string。 这两个string将具有相同的值,即“文本”。
String s = new String("text");
如果后来未被使用的话,则丢失(符合GC的条件)。
另一方面,string文字被重用。 如果你在你的类的多个地方使用"text"
,它实际上将是一个且只有一个string(即对该string在池中的多个引用)。
JLS
这个概念被JLS称为“实习”。
JLS 7相关文章3.10.5 :
而且,一个string文字总是引用类String的同一个实例。 这是因为string文字 – 或者更一般地说,是常量expression式(§15.28)的值的string – 被“interned”,以便使用方法String.intern共享唯一的实例。
例3.10.5-1。 string文字
由编制单位(第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
JVMS
JVMS 7 5.1说 :
string文字是对类String的一个实例的引用,并且是从类或接口的二进制表示中的CONSTANT_String_info结构(第4.4.3节)派生的。 CONSTANT_String_info结构给出了构成string文字的Unicode代码点序列。
Java编程语言要求相同的string文字(即包含相同的代码点序列的文字)必须引用同一类String(JLS§3.10.5)的实例。 此外,如果在任何string上调用String.intern方法,则结果是对同一个类实例的引用,如果该string显示为文字,则会返回该实例。 因此,以下expression式必须具有真值:
("a" + "b" + "c").intern() == "abc"
为了派生string文字,Java虚拟机检查由CONSTANT_String_info结构给出的代码点序列。
如果方法String.intern先前在包含与CONSTANT_String_info结构相同的Unicode代码点序列的String类实例上调用,则string文字派生的结果是对类String相同实例的引用。
否则,将创build一个包含由CONSTANT_String_info结构给出的Unicode代码点序列的类String的新实例; 该类实例的引用是string文字派生的结果。 最后,调用新的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
:创build一个新的string实例(以#2
作为参数) -
35
:将a
和c
作为常规对象与if_acmpne
进行比较
常量string的表示在字节码上非常神奇:
- 它有一个专用的CONSTANT_String_info结构,不像常规对象(例如
new String
) - 该结构指向包含数据的CONSTANT_Utf8_info结构 。 这是表示string的唯一必要数据。
而上面的JVMS报价似乎表示,只要Utf8指向的是相同的,则相同的实例由ldc
加载。
我已经做了类似的testing领域,并且:
-
static final String s = "abc"
通过ConstantValue属性指向常量表 - 非final字段没有该属性,但仍可以使用
ldc
进行初始化
结论 :对string池有直接的字节码支持,并且内存表示是有效的。
奖金:比较那整体池 ,没有直接的字节码支持(即没有CONSTANT_String_info
模拟)。
想想"bla"
是一个像Strings.createString("bla")
(伪)的魔法工厂。 工厂拥有一个所有string池,但这种方式创build。
如果被调用,它会检查池中是否已经有string。 如果为true,则返回此string对象,因此以这种方式获得的string的确是同一个对象。
如果没有,则在内部创build一个新的string对象,将其保存在池中,然后返回。 因此,当下一次查询相同的string值时,它将返回相同的实例。
手动创buildnew String("")
通过绕过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()将始终创build一个新的实例。
尽pipe从程序员的angular度来看它是一样的,但是它对性能有很大的影响。 你会想要几乎总是使用第一种forms。
String str = new String("hello")
它将检查是否string常量池已经包含string“你好”? 如果存在,则不会在string常量池中添加条目。 如果不存在,则会在String常量池中添加一个条目。
将在堆内存区域中创build一个对象,并在堆内存位置创build对象以创build对象。
如果你想str
引用包含在String常量池中的point对象,那么你必须显式调用str.intern();
String str = "world";
它将检查是否string常量池已经包含string“你好”? 如果存在,则不会在string常量池中添加条目。 如果不存在,则会在String常量池中添加一个条目。
在以上两种情况下,常量池中的str
参考指向String "world"
。
@布拉杰:我想你已经提到了另一种方式。 如果我错了,请纠正我
逐行创build对象:
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)