【问题标题】:What is the difference between "text" and new String("text")?\"text\" 和 new String(\"text\") 有什么区别?
【发布时间】:2022-12-04 21:45:17
【问题描述】:

下面这两个语句有什么区别?

String s = "text";

String s = new String("text");

【问题讨论】:

  • 任何人请回复这个。字符串 a = "Java";字符串 b = "Java"; System.out.println(a == b); true // 但是 System.out.println("a==b?"+a == b); //错误的...
  • 我不明白什么时候我添加了一些评论 ("a==b ?) => 我的结果变成了 FALSE。为什么?
  • @Energy 结果是false,因为操作顺序规定 + 运算符先行,连接“a==b?”用 a 创建一个字符串“a==b?Java”。然后表达式 "a==b?Java" == b 的计算结果为 false。
  • @AllisonB 知道了,非常感谢!

标签: java string


【解决方案1】:

new String("text"); 显式创建一个新的和引用不同的 String 对象实例; String s = "text"; 可以重用来自字符串常量池如果有的话。

非常稀有永远想使用 new String(anotherString) 构造函数。从 API:

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

相关问题


参照区分是什么意思

检查以下 sn-p:

    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) 构造函数。然而,需要再次说明的是,这是非常奇特的,很少是用心的。

参考

相关问题

【讨论】:

  • 如果我写: String s = new String("abc");现在我写: String s = "abc";请问String s = "abc";在字符串池中创建一个新的字符串文字?
  • 为什么没有人回答前面的问题?
  • @KaveeshKanwal 不,文字不会被复制。如您所见,有 2 个 "abc"s。只有其中一个会去String池,另外一个会引用它。然后是 s,这将是正确的新对象。
  • @Kaveesh Kanwal - String s = new String("abc") 只会创建一个值为“abc”的新字符串对象。第二条语句将检查字符串池中是否已经存在任何“abc”字符串文字。如果已经存在,则返回对现有的引用,如果不存在,则在字符串池中创建新的文字(“abc”)。希望它能解决您的问题!!
  • 没有关于它的“可能”。编译器必须池字符串文字。 JLS 3.10.5
【解决方案2】:

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

Further reading on Oracle community

【讨论】:

  • 好的答案..但想知道现在我要更改 str1="java 6" 的值然后它会更改 str4 的值吗?
  • 是的,我检查过它不会改变 str4 的值
  • @Braj 你能提供你的答案断言的文档吗?
  • @Braj:表中“堆”和“池”的标题应该是相反的吗?
  • 不正确。常量池是在编译时创建的,而不是在执行时创建的。不要对未引用的文本使用引用格式。
【解决方案3】:

String Constant Pool 中创建一个字符串

String s = "text";

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

String s = new String("text");

如果以后未使用,s 将丢失(符合 GC 条件)。

另一方面,字符串文字被重用。如果您在班级的多个地方使用"text",它实际上将是一个且只有一个字符串(即对池中同一字符串的多个引用)。

【讨论】:

  • 常量池中的字符串永远不会丢失。你的意思是说's'如果以后不使用就会丢失吗?
  • @EJP:是的,我的意思是“s”。感谢您的关注。我会纠正这个问题。
【解决方案4】:

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 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.intern 先前已在类 String 的实例上调用,其中包含与 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

注意如何:

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

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

上面的 JVMS 引用似乎是说,只要指向的 Utf8 相同,ldc 就会加载相同的实例。

我对字段进行了类似的测试,并且:

  • static final String s = "abc"通过ConstantValue Attribute指向常量表
  • 非final字段没有那个属性,但仍然可以用ldc初始化

结论: 字符串池有直接字节码支持,内存表示高效。

奖励:将它与 Integer pool 进行比较,后者没有直接的字节码支持(即没有 CONSTANT_String_info 类似物)。

【讨论】:

    【解决方案5】:

    任何字符串文字都在字符串文字池中创建,并且该池不允许任何重复项。因此,如果两个或多个字符串对象使用相同的文字值进行初始化,那么所有对象都将指向相同的文字。

    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”是否存在于字符串池中,并且将被创建,因为它不存在于以上案例。

    【讨论】:

      【解决方案6】:

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

      逐行创建对象:

      String str1 = new 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 不以任何方式涉及str2str3str4 的值。
      【解决方案7】:

      "bla"想象成一个像Strings.createString("bla")(伪)一样的魔法工厂。工厂拥有一个包含所有以这种方式创建的字符串的池。

      如果它被调用,它会检查池中是否已经存在具有该值的字符串。如果为真,则返回此字符串对象,因此以这种方式获得的字符串确实是同一个对象。

      如果没有,它会在内部创建一个新的字符串对象,将其保存在池中,然后返回。因此,下次查询相同的字符串值时,它返回相同的实例。

      手动创建 new String("") 通过绕过字符串文字池来覆盖此行为。因此,应始终使用 equals() 检查是否相等,它比较字符序列而不是对象引用相等性。

      【讨论】:

      • 您所指的“魔法工厂”无非就是 Java 编译器。把这个过程写成好像它发生在运行时是错误的。
      【解决方案8】:

      理解差异的一种简单方法如下:-

      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() 将始终创建一个新实例。

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:
          String str = new String("hello")
          

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

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

          如果你想str引用包含在字符串常量池中的点对象,那么必须显式调用str.intern();

          String str = "world";
          

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

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

          【讨论】:

          • “它”是 Java 编译器。字符串文字在编译时在常量池中创建一个唯一条目。将此过程描述为好像它发生在运行时是错误的。
          • 你能清楚地解释一下这篇文章中的错误吗?
          • 正如我已经说过的,这篇文章的错误在于字符串文字在编译时被合并。不像您的回答那样在执行代码时。
          • @EJP 感谢您的回复。你能指出答案中错误的确切行吗?我看到上面所有的答案都和我写的一样。请帮助,我想纠正我的理解。谢谢。
          • 你把整个过程写得好像这一切都发生在执行代码行时,正如我反复告诉你的那样,事实并非如此。您不能将所有这些都减少到答案中错误的单个“确切行”。
          【解决方案11】:

          当您将 String 存储为

          String string1 = "Hello";
          

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

          每当我们倾向于尝试生成另一个 String 作为

          String string2 = "Hello";
          

          JVM 会验证 String 常量池中是否存在任何具有常量值的 String 对象,如果存在,则 JVM 不会创建一个新对象,而是将现有对象的引用分配给新变量。

          当我们将 String 存储为

          String string = new String("Hello");
          

          使用 new 关键字,无论 String 常量池的内容如何,​​都会创建一个具有给定价格的全新对象。

          【讨论】:

            【解决方案12】:

            很抱歉迟到的答案,但非常需要答案。 首先我们需要知道一些Java.lang.String类规则。

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

            2. String is Immutable Object 即,如果值发生变化,则会创建一个新对象并返回给您,例如请参阅replace() and replaceAll() 函数等等。

            3. 这就造成了很多String Object in Modification的问题,所以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
                  } 
              }
              

            【讨论】:

              【解决方案13】:

              当您使用诸如new String("Hello World")SpotBugs代码分析工具之类的东西时,会抱怨性能问题.

              本期description表示对newconstant string的尊重

              使用 java.lang.String(String) 构造函数会浪费内存,因为 如此构建的对象在功能上将与 作为参数传递的字符串。只需使用参数 String 直接地。
              错误种类和模式:Dm - DM_STRING_CTOR

              【讨论】:

                猜你喜欢
                • 2022-07-21
                • 2022-01-21
                • 2012-03-18
                • 1970-01-01
                • 1970-01-01
                • 2012-12-29
                • 2018-03-17
                • 1970-01-01
                • 2019-04-18
                相关资源
                最近更新 更多