“text”和new String(“text”)之间有什么区别?

时间:2010-06-16 10:29:06

标签: java string

以下两个陈述之间有什么区别?

String s = "text";

String s = new String("text");

12 个答案:

答案 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将进入字符串常量池

以下快照可能会帮助您理解以可视方式以便长时间记住它。

enter image description here


逐行创建对象:

String str1 = new String("java5");

使用字符串文字" java5"在构造函数中,新的字符串值存储在字符串常量池中。 使用new运算符,在堆中使用" java5"创建一个新的字符串对象。作为价值。

String str2 = "java5"

参考" str2"指向已存储的字符串常量池

中的值
String str3 = new String(str2);

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

String str4 = "java5";

参考" str4"指向已存储的字符串常量池

中的值

总对象:堆 - 2,池 - 1

Further reading on Oracle community

答案 2 :(得分:15)

一个人在String Constant Pool

中创建一个字符串
String s = "text";

另一个在常量池("text")中创建一个字符串,在正常堆空间(s)中创建另一个字符串。两个字符串都具有相同的值,即“text”。

String s = new String("text");
如果以后未使用,

s将丢失(符合GC条件)。

另一方面,字符串文字被重用。如果在类的多个位置使用"text",它实际上只有一个String(即对池中相同字符串的多个引用)。

答案 3 :(得分:8)

JLS

这个概念被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

JVMS

JVMS 7 5.1 says

  

字符串文字是对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

请注意:

  • 03:加载相同的ldc #2常量(文字)
  • 12:创建一个新的字符串实例(以#2作为参数)
  • 35ac作为常规对象与if_acmpne进行比较

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

并且上面的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类规则。

  1. 字符串文字,例如String str="java";(我们仅使用双引号) 与String对象不同(我们使用new关键字) 例如String str=new String("java");

  2. String is Immutable Object,即,如果值更改,则会创建一个新对象并返回给您,例如,请参见replace() and replaceAll()函数以及更多其他内容。

  3. 这在修改中产生了许多字符串对象的问题,因此Java的创建者提出了一个名为StringPool的想法。 StringPool存储在堆区中,因为我们知道String是Char[]before java 9很长的读取时间)或byte [](after java 9阅读)。

  4. 字符串文字存储在StringPool中,而字符串对象存储在通常的堆对象区域中。

  5. 如果仅在字符串操作中将完成许多对象字符串初始化JVM堆,则Java开发团队提出了intern()解决方案,该解决方案将对StringPool的内存引用移动/更改。

    Program: Comparing String references to objects

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
    } 
}