【问题标题】:Strings are objects in Java, so why don't we use 'new' to create them?字符串是 Java 中的对象,那么我们为什么不使用 'new' 来创建它们呢?
【发布时间】:2011-01-01 19:51:11
【问题描述】:

我们通常使用new 关键字创建对象,例如:

Object obj = new Object();

字符串是对象,但我们不使用new 来创建它们:

String str = "Hello World";

这是为什么?我可以用new 做一个字符串吗?

【问题讨论】:

  • 因为字符串文字已经是对象了。
  • 请注意,new String(...) 已用于在对大字符串进行子串化时规避实现细节。这在 Java 7 中已修复,不再需要。
  • 我是这篇文章的第 100 个赞。 :)

标签: java string core new-operator


【解决方案1】:

这是一条捷径。最初并不是这样,但 Java 改变了它。

这个FAQ 简单地谈论它。 Java 规范指南也谈到了它。但是我在网上找不到。

【讨论】:

  • 链接断开,我不知道有任何其他证据表明它曾经被更改过。
  • @EJP 如果有用的话,它仍然在wayback machine 中。
【解决方案2】:

随意创建一个新的字符串

String s = new String("I'm a new String");

通常的符号s = "new String"; 或多或少是一种方便的快捷方式 - 出于性能原因应该使用它,除了那些非常罕见的情况,您真的需要符合等式条件的字符串

(string1.equals(string2)) && !(string1 == string2)

编辑

回应评论:这不是打算作为建议,而只是对提问者论文的直接回应,我们不使用“新”关键字 表示字符串,这根本不是真的。希望这个编辑(包括上面的)能澄清一点。顺便说一句 - 对于上述关于 SO 的问题,有几个很好的更好的答案。

【讨论】:

  • -1 - 糟糕的建议。你不应该“随意”使用new String(...),除非你的应用程序要求你创建一个具有不同身份的字符串。
  • 我知道。编辑了帖子以进行澄清。
【解决方案3】:

字符串需要进行一些优化(因为需要更好的短语)。请注意,与其他对象不同,String 还具有运算符重载(对于 + 运算符)。所以这是一个非常特殊的情况。

【讨论】:

  • + 实际上是一个运算符,它被转换为 StringBuilder.append(..) 调用。
【解决方案4】:

语法糖。

String s = new String("ABC");

语法仍然可用。

【讨论】:

  • 这不太对。 s=new String("ABC") 不会给出与 s="ABC" 相同的结果。见丹本的评论。
  • 另外,有点讽刺的是,它会首先创建一个表示“ABC”内联的 String 实例 - 然后将其作为参数传递给构造函数调用,该构造函数调用将创建一个返回值相同的 String。跨度>
  • 此构造函数的有效用例是String small = new String(huge.substring(int, int));,它允许您从原始huge String 中回收大底层char[]
  • @PascalThivent 是,但 Java 8 不再共享。它不再共享数组(为其他优化做准备,例如 G1 的自动字符串重复数据删除或即将到来的字符串压缩)。
  • @AndrzejDoyle 不正确。编译器为文字创建对象。
【解决方案5】:

除了已经说过的之外,Java 中的 String literals [即,类似 "abcd" 但不像 new String("abcd")] 的字符串是 intern - 这意味着每次您引用“abcd ",您将获得对单个 String 实例的引用,而不是每次都引用一个新实例。所以你将拥有:

String a = "abcd";
String b = "abcd";

a == b; //True

如果你有

String a = new String("abcd");
String b = new String("abcd");

那么就有可能

a == b; // False

(如果有人需要提醒,请始终使用.equals() 来比较字符串;== 测试物理平等)。

实习字符串字面量很好,因为它们经常被多次使用。例如,考虑(人为的)代码:

for (int i = 0; i < 10; i++) {
  System.out.println("Next iteration");
}

如果我们没有对字符串进行实习,“下一次迭代”需要实例化 10 次,而现在它只会实例化一次。

【讨论】:

  • 使用String a = new String("abcd"),是否意味着内存中存在两个内容相似的字符串。
  • 对 - 编译器不一定会检查这样的字符串是否已经被实习过(尽管你当然可以写一个这样的)。
  • 是的,这种优化是可能的,因为字符串是不可变的,因此可以毫无问题地共享。共享的“asdf”处理是“享元”设计模式的实现。
  • 没有人说不可能,只是说不能保证。那是你的反对票吗?
  • “== 对象相等性测试”是什么意思?这对我来说似乎不是真的,但也许你的意思与这看起来的意思不同。
【解决方案6】:

您仍然可以使用new String("string"),但是如果没有字符串文字,创建新字符串会更加困难......您必须使用字符数组或字节 :-) 字符串文字还有一个附加属性:所有相同的字符串文字来自任何类都指向同一个字符串实例(它们是实习的)。

【讨论】:

    【解决方案7】:

    字符串是 Java 中的“特殊”对象。 Java 设计者明智地决定字符串的使用如此频繁,以至于他们需要自己的语法和缓存策略。当你声明一个字符串时:

    String myString = "something";
    

    myString 是对值为“something”的 String 对象的引用。如果您稍后声明:

    String myOtherString = "something";
    

    Java 足够聪明,可以确定 myString 和 myOtherString 相同,并将它们作为同一个对象存储在全局 String 表中。它依赖于您无法修改字符串来执行此操作的事实。这减少了所需的内存量,并且可以更快地进行比较。

    如果你写

    String myOtherString = new String("something");
    

    Java 将为您创建一个全新的对象,与 myString 对象不同。

    【讨论】:

    • 嘿......它不需要“无限智慧”来认识到需要某种对字符串文字的语法支持。几乎所有其他严肃的编程语言设计都支持某种字符串文字。
    • 夸张已被简化为眩晕,船长 :)
    【解决方案8】:

    在 Java 中,字符串是一种特殊情况,有许多规则仅适用于字符串。双引号导致编译器创建一个 String 对象。由于 String 对象是不可变的,这允许编译器实习多个字符串,并构建一个更大的字符串池。两个相同的 String 常量将始终具有相同的对象引用。如果您不希望出现这种情况,则可以使用 new String(""),这将在运行时创建一个 String 对象。 intern() 方法曾经很常见,用于根据字符串查找表检查动态创建的字符串。一旦一个字符串在 interned 中,对象引用将指向规范的 String 实例。

        String a = "foo";
        String b = "foo";
        System.out.println(a == b); // true
        String c = new String(a);
        System.out.println(a == c); // false
        c = c.intern();
        System.out.println(a == c); // true
    

    当类加载器加载一个类时,所有的字符串常量都被添加到字符串池中。

    【讨论】:

    • "双引号导致编译器创建一个字符串对象。"被低估的评论
    【解决方案9】:

    几乎不需要 new 字符串,因为字面量(引号中的字符)已经是加载宿主类时创建的 String 对象。在文字和 don 上调用方法是完全合法的,主要区别在于文字提供的便利。如果我们必须创建一个字符数组并逐个字符地填充它,然后他们执行一个新的字符串(字符数组),那将是一个巨大的痛苦和浪费。

    【讨论】:

      【解决方案10】:

      文字池包含任何未使用关键字new 创建的字符串。

      有一个区别:没有新引用的字符串存储在字符串字面量池中,而带有新引用的字符串表示它们在堆内存中。

      带有 new 的字符串与任何其他对象一样位于内存中的其他位置。

      【讨论】:

        【解决方案11】:
        String a = "abc"; // 1 Object: "abc" added to pool
        
        String b = "abc"; // 0 Object: because it is already in the pool
        
        String c = new String("abc"); // 1 Object
        
        String d = new String("def"); // 1 Object + "def" is added to the Pool
        
        String e = d.intern(); // (e==d) is "false" because e refers to the String in pool
        
        String f = e.intern(); // (f==e) is "true" 
        
        //Total Objects: 4 ("abc", c, d, "def").
        

        希望这能消除一些疑虑。 :)

        【讨论】:

        • String d = new String("def"); // 1 个对象 + "def" 被添加到池中 -> 此处的 "def" 仅当它还没有时才会添加到池中
        • @southerton 毫无意义。它已经在游泳池里了。它是由编译器放置在那里的。
        • @EJP 为什么 (e==d) 在这里是假的?它们都引用池中的同一个对象“def”对吗?
        • String c = new String("abc"); // 1 个对象 ... 这个说法正确吗?如果已经从常量池中引用了“abc”,那么 inter 方法有什么用?
        • @Raja No,d 指的是一个新的字符串,而e 则返回到它的实习版本。
        【解决方案12】:

        因为 String 在 java 中是一个不可变的类。

        现在为什么它是不可变的? 由于 String 是不可变的,所以它可以在多个线程之间共享,我们不需要在外部同步 String 操作。 As String 也用于类加载机制。因此,如果 String 是可变的,那么 java.io.writer 可以更改为 abc.xyz.mywriter

        【讨论】:

          【解决方案13】:
          TString obj1 = new TString("Jan Peter");                
          TString obj2 = new TString("Jan Peter");                    
          
          if (obj1.Name == obj2.Name)                 
              System.out.println("True");
          else                
              System.out.println("False");
          

          输出:

          是的

          我创建了两个单独的对象,它们都有一个字段(参考)“名称”。因此,即使在这种情况下,“Jan Peter”也是共享的,如果我了解 java 的处理方式..

          【讨论】:

            【解决方案14】:

            我们通常使用字符串字面量来避免创建不必要的对象。如果我们使用 new 操作符来创建 String 对象,那么它每次都会创建一个新对象。

            例子:

            String s1=“Hello“;
            String s2=“Hello“;
            String s3= new String(“Hello“);
            String s4= new String(“Hello“);
            

            以上代码在内存中:

            【讨论】:

              【解决方案15】:

              StringPool 是使用 Java 中的 Hashmap 实现的。如果我们始终使用新关键字创建,则它不会在字符串池中搜索并为其创建新内存,如果我们正在运行内存密集型操作并且如果我们使用新关键字创建所有字符串会影响性能我们的应用程序。因此,建议不要使用新关键字来创建字符串,因为只有这样它才会进入字符串池,而字符串池又是一个 Hashmap (节省内存,想象一下如果我们有很多使用新关键字创建的字符串),它将被存储并如果字符串已经存在,它的引用(通常驻留在堆栈内存中)将返回到新创建的字符串。 所以这样做是为了提高性能。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2010-11-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-02-07
                • 2019-09-02
                • 2016-12-18
                相关资源
                最近更新 更多