下面这两种说法有什么不同?

String s = "text";

String s = new String("text");

新的字符串(“文本”); 显式地创建String对象的一个新的引用不同的实例;字符串s = "text";可以重用字符串常量池中的实例(如果有)。

你很少会想要使用新的String(anotherString)构造函数。来自API:

String(String original):初始化新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要original的显式副本,否则使用此构造函数是不必要的,因为字符串是不可变的。

相关问题

Java字符串:“字符串s =新字符串(“愚蠢的”);” 字符串是Java中的对象,所以为什么我们不使用' new '来创建它们呢?


什么是指涉区别

检查下面的代码片段:

    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而不是== string,则可以使用新的string (anotherString)构造函数。然而,需要再次说明的是,这是非常奇怪的,而且很少是故意的。

参考文献

JLS 15.21.3引用相等操作符==和!= 类对象-布尔对象(等于)

相关问题

Java字符串。等于和== 我如何比较字符串在Java?


一个是在字符串常量池中创建一个字符串

String s = "text";

另一个在常量池中创建一个字符串("text"),在普通堆空间中创建另一个字符串(s)。两个字符串的值相同,即"text"。

String s = new String("text");

如果以后未使用,则会丢失(符合GC的条件)。

另一方面,字符串字面值被重用。如果你在你的类的多个地方使用“text”,它实际上将是一个且只有一个String(即在池中多次引用同一个字符串)。


把“bla”想象成一个魔法工厂,就像Strings.createString(“bla”)(pseudo)。工厂保存了所有以这种方式创建的字符串池。

如果它被调用,它会检查池中是否已经有这个值的字符串。如果为真,则返回此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

因此new String()总是会创建一个新实例。


尽管从程序员的角度来看,它们看起来是一样的,但它对性能有很大的影响。你几乎总是想用第一种形式。


字符串字面值将进入字符串常量池。

下面的快照可能会帮助你从视觉上理解它,从而更长时间地记住它。


逐行创建对象:

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社区的进一步阅读


@Braj:我想你已经提到了另一种方式。如果我错了,请指正

逐行创建对象:

字符串 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)

字符串 str3 = 新字符串(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)

String str = new String("hello")

它将检查字符串常量池是否已经包含字符串“hello”? 如果存在,则不会在String常量池中添加条目。如果不存在,它将在String常量池中添加一个条目。

对象将在堆内存区域中创建,str引用指向在堆内存位置中创建的对象。

如果你想要str引用String常量池中包含的点对象,则必须显式调用str.intern();

String str = "world";

它将检查字符串常量池是否已经包含字符串“hello”? 如果存在,则不会在String常量池中添加条目。如果不存在,它将在String常量池中添加一个条目。

在上述两种情况下,str引用指向常量池中存在的字符串“world”。


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 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 string1 = "Hello";

然后JVM在称为String常量池的单独内存块中创建一个具有给定价格的String对象。

当我们有尝试生成另一个String的倾向时

String string2 = "Hello";

JVM验证String常量池中是否存在价格恒定的String对象,如果存在,JVM不会创建一个全新的对象,而是将现有对象的引用赋值给新变量。

当我们存储String为

String string = new String("Hello");

使用new关键字,不管String常量池的内容是什么,都会生成一个具有给定价格的全新对象。


抱歉回复晚了,但我急需回复。 首先,我们需要知道一些Java.lang.String类规则。

String Literals e.g.String str="java"; (we use only double Quotes) are different from String Object (we use new keyword) e.g. String str=new String("java"); String is Immutable Object i.e. If value changes a new Object is created and returned to you eg See replace() and replaceAll() functions and many more. This creates a problem of many String Object in Modification, So creators of Java came up an Idea was called StringPool. StringPool is stored in heap area where object reference data will be stored as we know String is Char[](before java 9 very Long to read) or byte[](after java 9 short to read). String literals are stored in StringPool and String Objects are stored in as usual heap Object Area. If there are many Object String Initialization JVM heap will be finished in String Operations only, Java Development team came up with intern() solution this moves/changes memory reference to StringPool. Program: Comparing String references to objects

另一个更好地理解java.lang.String的好链接

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

当你使用一些东西,如new String(“Hello World”),SpotBugs代码分析工具,抱怨一个性能问题。

这个问题的描述显示了新字符串和常量字符串的区别

使用java.lang.String(String)构造函数会浪费内存,因为 这样构造的对象将在功能上难以区分 作为参数传递的字符串。只需使用参数String 直接。 错误类型和模式:Dm - DM_STRING_CTOR