【问题标题】:What is the difference between "text" and new String("text")?“文本”和新字符串(“文本”)有什么区别?
【发布时间】:2015-05-30 00:14:15
【问题描述】:

下面这两种说法有什么区别?

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。只有其中一个会进入字符串池,另一个会引用它。然后是s,它将是适当的新对象。
  • @Kaveesh Kanwal - String s = new String("abc") 只会创建一个值为“abc”的新字符串对象。第二条语句将检查字符串池中是否已经存在任何“abc”字符串文字。如果已经存在,则返回对现有的引用,如果不存在,则在字符串池中创建新的文字(“abc”)。希望它能解决您的问题!
  • 没有“可能”。编译器 must 池字符串文字。 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="java6" 的值,然后它会更改 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

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 结构给出的代码点序列。

  • 如果先前已在包含与 CONSTANT_String_info 结构给出的相同的 Unicode 代码点序列的类 String 的实例上调用方法 String.intern,则字符串文字派生的结果是对该类的引用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 作为参数)
  • 35: acif_acmpne 比较为常规对象

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

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

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

  • static final String s = "abc" 通过ConstantValue Attribute 指向常量表
  • 非最终字段没有该属性,但仍可以使用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")
          

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

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

          如果您希望str 引用包含在字符串常量池中的点对象,则必须显式调用str.intern();

          String str = "world";
          

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

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

          【讨论】:

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

          当您将字符串存储为时

          String string1 = "Hello";
          

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

          每当我们倾向于尝试生成另一个字符串时

          String string2 = "Hello";
          

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

          当我们将字符串存储为

          String string = new String("Hello");
          

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

          【讨论】:

            【解决方案12】:

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

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

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

            3. 这就造成了很多String Object在Modification中的问题,所以Java的创造者想出了一个叫StringPool的idea。 StringPool 存储在堆区域中,我们知道 String 是 Char[](before java 9 读取时间很长) 或 byte[](after java 9 读取时间很短)。

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

            5. 如果有很多 Object String Initialization JVM heap 将仅在 String Operations 中完成,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
                } 
            }
            

            【讨论】:

              猜你喜欢
              • 2011-11-13
              • 1970-01-01
              • 2019-06-13
              • 2011-09-05
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多