【问题标题】:Difference between string object and string literal [duplicate]字符串对象和字符串文字之间的区别[重复]
【发布时间】:2011-03-18 21:25:30
【问题描述】:

有什么区别

String str = new String("abc");

String str = "abc";

【问题讨论】:

  • 简洁答案:字符串对象是一个变量;字符串文字是 constant(引号之间的固定字符序列)。 More details
  • 字符串literal是字符串object,但是Stringobject不一定是Stringliteral 。一旦分配给引用变量,就几乎不可能判断给定的 String 对象是否是 literal
  • 这被标记为与它根本不重复的东西的重复。并不是说这是一个很好的问题,但其他人已经正确地将这个列为重复项,并且重复项列表应该以真正重复的内容结尾。
  • 我在 Sybex 考试中答错了这个问题,因为我说它是错误的:All String literals are automatically instantiated into a String object.。考试似乎认为它总是正确的,即使一个文字被实习到一个已经存在的对象上?

标签: java string string-literals


【解决方案1】:

由于字符串是不可变的,当你这样做时:

String a = "xyz"

在创建字符串时,JVM 在字符串池中搜索是否已经存在字符串值"xyz",如果存在,'a' 将只是该字符串的引用,并且不会创建新的 String 对象。

但如果你说:

String a = new String("xyz")

你强制 JVM 创建一个新的 String 引用,即使 "xyz" 在其池中。

更多信息请阅读this

【讨论】:

【解决方案2】:

在第一种情况下,创建了两个对象。

在第二种情况下,它只是一个。

虽然str 都指的是"abc"

【讨论】:

  • 你能解释一下如何在第一种情况下创建 obj 吗?
  • @GautamSavaliya 在第一种情况下,将在字符串池中创建一个对象以缓存字符串以便在代码中进一步重用,并在堆内存中创建另一个对象。您可以参考这个问题stackoverflow.com/questions/2486191/java-string-pool 了解有关字符串池的更多详细信息。
【解决方案3】:

当你使用字符串字面量时,字符串可以是interned,但当你使用new String("...") 时,你会得到一个新的字符串对象。

在这个例子中,两个字符串字面量都指向同一个对象:

String a = "abc"; 
String b = "abc";
System.out.println(a == b);  // true

这里创建了 2 个不同的对象,它们有不同的引用:

String c = new String("abc");
String d = new String("abc");
System.out.println(c == d);  // false

一般来说,您应该尽可能使用字符串文字表示法。它更易于阅读,并让编译器有机会优化您的代码。

【讨论】:

  • 在实践中,您通常看到new String(...) 的使用不是因为有人想要这里描述的行为,而是因为他们不知道字符串是不可变的。所以你会看到b = new String(a); b = b.substring(2); 而不仅仅是b = a.substring(2) 之类的东西,因为作者可能认为 substring 方法将修改调用它的实例。同样,虽然这是真的"abc" == "abc" 我会说 一般 依赖于此而不是使用 equals(...) 的代码很聪明并且容易混淆事物(静态最终“常量”是例外)。
  • @broofa:这在 JavaScript 中有什么用处?
  • 这个概念的重要性是什么?节省内存?
  • 你也可以使用new String("...").intern()...
  • 当你使用字符串字面量时,字符串 ,根据 JLS 的规则。没有“机会”。编译器有义务汇集这些文字。
【解决方案4】:

字符串字面量 是 Java 语言的概念。这是一个字符串文字:

"a String literal"

String 对象java.lang.String 类的单个实例。

String s1 = "abcde";
String s2 = new String("abcde");
String s3 = "abcde";

所有都有效,但略有不同。 s1 将引用一个 interned 字符串对象。这意味着,字符序列"abcde" 将被存储在一个中心位置,并且每当再次使用相同的文字"abcde" 时,JVM 不会创建一个新的 String 对象,而是使用 cached 字符串。

s2 保证是一个新的字符串对象,所以在这种情况下我们有:

s1 == s2 // is false
s1 == s3 // is true
s1.equals(s2) // is true

【讨论】:

  • 很多很多语言都有字符串字面量的概念:)
  • 那么这是否意味着文字字符串“abc”仍然是像new String("abc")这样的对象,唯一的区别是它存储在实习池而不是堆中?
  • @yifei 是的,就是这个意思。
  • 所以意思是string literal is better 因为它再次使用相同的对象而不是新空间?
  • 那么在你的情况下,s2引用的字符数组'abcde'位于堆而不是字符串池中?所以如果你创建 100 个像 new String("abc") 这样的 String 对象,在堆中,abc 会有 100 个副本?
【解决方案5】:

长答案可用here,所以我会给你一个短答案。

当你这样做时:

String str = "abc";

您正在对String 调用intern() 方法。此方法引用了一个内部池,其中包含 String 对象。如果您调用intern() 的字符串已经存在于池中,那么对该String 的引用将分配给str。如果没有,则将新的String 放入池中,然后将对其的引用分配给str

给定以下代码:

String str = "abc";
String str2 = "abc";
boolean identity = str == str2;

当您通过执行== 来检查对象身份时(您实际上是在问:这两个引用是否指向同一个对象?),您会得到true

但是,您不需要 intern() Strings。您可以通过执行以下操作强制在堆上的新 Object 上创建:

String str = new String("abc");
String str2 = new String("abc");
boolean identity = str == str2;

在这种情况下,strstr2 是对不同 Objects 的引用,它们都没有被实习,因此当您使用 == 测试 Object 身份时,你会得到false

就良好的编码习惯而言:不要使用== 来检查字符串是否相等,而是使用.equals()

【讨论】:

  • 您并没有真正通过引用文字来调用 intern() 。您依赖于编译器已经将它们合并并在常量区域中创建了 String 对象。
  • EJB,从什么时候开始编译器创建对象?编译后的字节码可能会在 10 年后在另一台机器上运行。创建 String 对象是 JVM 的工作。并且根据机器语言规范(3.10.5),A string literal is a reference to an instance of class String。该规范甚至承诺它将是跨不同类和包的相同实例。您可能正在考虑“常量表达式”。 编译器将代码"Hello" + " World"重写为"Hello World"
  • @MartinAnderson 从JLS required it to 开始,如果不是更早的话,它最早发生在 1995 年。遇到字符串文字时的行为是 (1) 编译器在类内进行池化,以及 (2) 类加载器在 JVM 内进行池化。所有这些动作都发生在通过执行引用它的代码行“遇到”字符串文字之前很久。
  • 我赞成这个答案只是因为解释。感谢您让这个概念更加清晰。
【解决方案6】:

String 是 Java 中不同于其他编程语言的一个类。所以对于每个类,对象声明和初始化都是

String st1 = new String();

String st2 = new String("Hello"); 
String st3 = new String("Hello");

这里,st1st2st3 是不同的对象。

即:

st1 == st2 // false
st1 == st3 // false
st2 == st3 // false

因为st1st2st3 引用了 3 个不同的对象,而== 检查内存位置是否相等,因此得出了结果。

但是:

st1.equals(st2) // false
st2.equals(st3) // true

这里.equals()方法检查内容,以及st1 = ""st2 = "hello"st3 = "hello"的内容。结果就是这样。

在字符串声明的情况下

String st = "hello";

这里调用String类的intern()方法,检查"hello"是否在intern pool中,如果没有,则添加到intern pool中,如果intern pool中有“hello”,则st 将指向现有"hello" 的内存。

所以在以下情况下:

String st3 = "hello";
String st4 = "hello"; 

这里:

st3 == st4 // true

因为st3st4 指向同一个内存地址。

还有:

st3.equals(st4);  // true as usual

【讨论】:

  • @CaZbaN1 +1 用于解释用于确定值是否存在于堆上的实习方法 intern()。
【解决方案7】:

以下是一些比较:

String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");

System.out.println(s1 == s2); //true
System.out.println(s1.equals(s2)); //true

System.out.println(s1 == s3);   //false
System.out.println(s1.equals(s3)); //true

s3 = s3.intern();
System.out.println(s1 == s3); //true
System.out.println(s1.equals(s3)); //true

intern() 被调用时,引用会改变。

【讨论】:

    【解决方案8】:

    根据String class documentation,它们是等价的。

    String(String original) 的文档还说:除非需要原始的显式副本,否则不需要使用此构造函数,因为字符串是不可变的。

    寻找其他回应,因为 Java 文档似乎具有误导性:(

    【讨论】:

    • -1 在某种意义上是,在其他意义上不是。所以你的回答有误导性。
    • 它们绝对不等价。每次执行时都会构造一个新字符串,而不会。所涉及的字符串将相等,但这并不意味着两个表达式的行为方式完全相同。
    • 所以文档是谎言 :( 或者我不理解字符串文档的前两段。
    • @Michal:是的,文档非常具有误导性。并且该构造函数 可以 在某些情况下很有用,因为它可以有效地将新字符串“修剪”到合适的大小。
    • @Carl, @Michael:详细说明 Jon Skeet 的评论:使用 str.substring() 返回一个新字符串,引用 str 的相同 char 数组。如果您不再需要 str,则使用 new String(str.substring(..))。否则,您可能会使用比必要更多的内存。
    【解决方案9】:

    "abc" 是文字字符串。

    在 Java 中,这些文字字符串在内部进行池化,并且在您在代码中声明该字符串文字的任何地方都使用 "abc" 的相同 String 实例。所以"abc" == "abc" 将始终为真,因为它们都是同一个 String 实例。

    使用String.intern() 方法,您可以将任何您喜欢的字符串添加到内部池化字符串中,这些字符串将保留在内存中,直到 java 退出。

    另一方面,使用new String("abc") 将在内存中创建一个新的字符串对象,这在逻辑上与"abc" 字面量相同。 "abc" == new String("abc") 将始终为 false,因为尽管它们在逻辑上相等,但它们引用不同的实例。

    在字符串文字周围包装一个 String 构造函数没有任何价值,它只是不必要地使用了比它需要的更多的内存。

    【讨论】:

    • “内部池化的字符串将保存在内存中,直到 java 退出”。我认为至少对于现代 JVM,这不再有效,因为 GC 还会在 perm 区域收集未使用的对象。你能确认一下吗?
    【解决方案10】:

    String 对象和字符串字面量之间存在细微差别。

    String s = "abc"; // creates one String object and one reference variable
    

    在这个简单的例子中,“abc”将进入池中,s 将引用它。

    String s = new String("abc"); // creates two objects,and one reference variable
    

    在这种情况下,因为我们使用了new关键字,Java会创建一个新的String对象 在普通(非池)内存中,s 将引用它。此外,文字“abc”将 放在游泳池里。

    【讨论】:

      【解决方案11】:

      String s = new String("FFFF") 创建 2 个对象:"FFFF" 字符串和 String 对象,它们指向"FFFF" 字符串,所以它就像指针指向指针(引用引用,我不喜欢术语)。

      据说你永远不应该使用new String("FFFF")

      【讨论】:

      • 并非如此。 FFFF 已经由编译器创建。在运行时创建零个或一个对象。
      • 错了,只有一个对象和一个引用变量
      【解决方案12】:

      除了已经发布的答案,另见thisjavaranch 上的优秀文章。

      【讨论】:

        【解决方案13】:

        一些反汇编总是很有趣...

        $ cat Test.java 
        public class Test {
            public static void main(String... args) {
                String abc = "abc";
                String def = new String("def");
            }
        }
        
        $ javap -c -v Test
        Compiled from "Test.java"
        public class Test extends java.lang.Object
          SourceFile: "Test.java"
          minor version: 0
          major version: 50
          Constant pool:
        const #1 = Method  #7.#16;  //  java/lang/Object."<init>":()V
        const #2 = String  #17;     //  abc
        const #3 = class   #18;     //  java/lang/String
        const #4 = String  #19;     //  def
        const #5 = Method  #3.#20;  //  java/lang/String."<init>":(Ljava/lang/String;)V
        const #6 = class   #21;     //  Test
        const #7 = class   #22;     //  java/lang/Object
        const #8 = Asciz   <init>;
        ...
        
        {
        public Test(); ...    
        
        public static void main(java.lang.String[]);
          Code:
           Stack=3, Locals=3, Args_size=1
            0:    ldc #2;           // Load string constant "abc"
            2:    astore_1          // Store top of stack onto local variable 1
            3:    new #3;           // class java/lang/String
            6:    dup               // duplicate top of stack
            7:    ldc #4;           // Load string constant "def"
            9:    invokespecial #5; // Invoke constructor
           12:    astore_2          // Store top of stack onto local variable 2
           13:    return
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-01-29
          • 2015-09-08
          • 1970-01-01
          • 1970-01-01
          • 2016-10-22
          • 2018-11-12
          相关资源
          最近更新 更多