字符串是Java中的对象,那么为什么不使用'new'来创建它们呢?


104

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

Object obj = new Object();

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

String str = "Hello World";

为什么是这样?我可以用做一个琴弦new吗?



1
因为字符串文字已经是对象。
罗恩侯爵,

1
请注意,new String(...)在对大字符串进行子字符串化时,已用于绕过实现细节。此问题已在Java 7中修复,不再需要。
托尔比约恩Ravn的安德森

我是这篇文章的第100个赞。:)
Mukit09 '19

Answers:


130

除了已经说过的内容外,还对Java中的字符串文字 [即,类似"abcd"但不喜欢的字符串new String("abcd")]进行了插值-这意味着,每次引用“ 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()比较字符串;==测试物理相等性)。

Interning String文字很好,因为它们经常被多次使用。例如,考虑(人为)代码:

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

如果我们没有String的内部,则“下一次迭代”将需要实例化10次,而现在只需要实例化一次。


1
通过使用String a = new String(“ abcd”),是否表示内存中存在两个内容相似的字符串。
更改了2010年

1
正确-编译器不一定会检查是否已实习过这样的String(尽管您当然可以编写一个这样做的字符串)。
danben 2010年

是的,这种优化是可行的,因为字符串是不可变的,因此可以毫无问题地共享。共享的“ asdf”处理是“轻量级”设计模式的实现。
曼努埃尔·奥尔丹娜

没有人说这是不可能的,只是没有保证。那是你的反对票吗?
danben 2010年

您所说的“ ==对象相等性测试”是什么意思?在我看来,这不是真的,但也许您的意思与这似乎不同。
达伍德·伊本·卡里姆

32

字符串是Java中的“特殊”对象。Java设计人员明智地决定使用Strings的频率很高,以至于他们需要自己的语法和缓存策略。通过以下方式声明字符串时:

String myString = "something";

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

String myOtherString = "something";

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

相反,如果您写

String myOtherString = new String("something");

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


嘿...不需要“无限智慧”就可以识别对字符串文字的某种语法支持。几乎所有其他严肃的编程语言设计都支持某种字符串文字。
斯蒂芬·C

12
夸张的
玩意

16
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").

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


字符串d =新的String(“ def”); //将1个对象+“ def”添加到池中->仅在此“ def”还没有添加到池中
southerton 2015年

@southerton毫无意义。它已经在游泳池里了。它由编译器放置在此处。
罗恩侯爵,

@EJP为什么(e == d)在这里是假的?它们都引用池中的同一对象“ def”吧?
拉贾

字符串c =新的String(“ abc”); // 1对象...此语句正确吗?如果已经从常量池中引用了“ abc”,那么inter方法的用途是什么?
Prashanth Debbadwar

6

这是捷径。最初不是那样的,但是Java改变了它。

常见问题解答简要讨论了它。Java规范指南也对此进行了讨论。但是我无法在线找到它。


2
链接断开,并且我不知道有任何其他证据表明该链接已更改。
罗恩侯爵,

1
@EJP 如果有用的话,它仍在回送机器中
Arjan

6

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


1
+实际上是一个运算符,被转换为StringBuilder.append(..)调用。
whiskeysierra

5

我们通常使用String文字以避免创建不必要的对象。如果我们使用new运算符创建String对象,那么它将每次都创建新对象。

例:

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

对于内存中的上述代码:

在此处输入图片说明


2

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

当类加载器加载类时,所有String常量都将添加到String池中。


“双引号使编译器创建一个String对象。” 被低估的评论
csguy

0

语法糖。的

String s = new String("ABC");

语法仍然可用。


1
这不太正确。s = new String(“ ABC”)不会为您提供与s =“ ABC”相同的结果。参见danben的评论。
史蒂夫·B。2010年

2
而且,有点讽刺的是,它将首先创建一个表示“ ABC”内联的String实例-然后将其作为参数传递给构造函数调用,该构造函数将创建一个返回值相同的String。
Andrzej Doyle'1

1
此构造函数的有效用例是String small = new String(huge.substring(int, int));,它允许您char[]从原始hugeString中回收较大的基础。
Pascal Thivent 2010年

1
@PascalThivent是,但在Java 8中不再可用。它不再共享数组(为其他优化做准备,例如使用G1进行自动字符串重复数据删除或即将进行的字符串压缩)。
eckes

@AndrzejDoyle不正确。编译器为文字创建对象。
罗恩侯爵,

0

您仍然可以使用new String("string"),但是在没有字符串文字的情况下创建新字符串将更加困难...您将不得不使用字符数组或字节:-)字符串文字具有一个附加属性:从任何类指向同一字符串的所有相同字符串文字实例(他们被拘留)。


0

几乎不需要新的字符串,因为文字(引号中的字符)已经是在加载主机类时创建的String对象。在文字上调用方法和don是完全合法的,主要区别在于文字提供的便利性。如果我们必须创建一个char数组并用char填充char,然后他们做一个新的String(char array),这将是一个很大的痛苦和浪费的例程。


0

随意创建一个新的字符串

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

通常的表示法s = "new String";或多或少是一种便捷的快捷方式-出于性能原因,应使用该快捷方式,但在极少数情况下,除非您确实需要符合该等式的字符串

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

编辑

对此评论作出回应:这并不是要作为建议,而只是对提问者论点的直接回应,即我们不对字符串使用'new'关键字,这根本不是事实。希望此编辑(包括上面的内容)能使您对此有所了解。顺便说一句-关于SO的上述问题有两个很好的更好的答案。


4
-1-不好的建议。new String(...)除非您的应用程序要求您创建具有唯一标识的String,否则您不应“随意”使用。
斯蒂芬·C 2010年

1
我知道。编辑帖子以进行澄清。
Andreas Dolk

0

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

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

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


0

因为String是Java中的不可变类。

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


0
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");

输出:

真正

我创建了两个单独的对象,它们都有一个field(ref)'Name'。因此,即使在这种情况下,如果我了解java的处理方式,也可以共享“ Jan Peter”。


0

那么StringPool是使用Java中的Hashmap实现的。如果我们始终使用新的关键字创建它,则它不在字符串池中搜索并为其创建新的内存,如果我们正在运行占用大量内存的操作,并且正在使用new关键字创建所有会影响性能的字符串,则稍后可能需要我们的应用程序。因此建议不要使用新的关键字来创建字符串,因为只有这样,它才会进入字符串池,而该字符串池又是一个Hashmap,(保存了内存,假设我们有很多用new关键字创建的字符串)将在此处存储和如果字符串已经存在,则该字符串的引用(通常位于堆栈内存中)将返回到新创建的字符串。这样做是为了提高性能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.