当 new String("hello") 间接指向 "hello" 时,使用 new String("hello") 是否比简单的 "hello" 完全没用?

Does use of new String("hello") is completely useless over simple "hello", when it is indirectly pointing to "hello"?

执行后 String S1 = "hello"; JVM 将在 SCP 中创建一个 String 对象,该对象将在 value 字段中保存一个 char 数组,如

s1.value = {'h', 'e', 'l', 'l', 'o'}

当我们说

String s2 = new String("hello");

并且根据String的源代码,class构造函数执行后s2.value也会变成"hello".value,类似于s1.value

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

所以每次我们使用 new 创建 String 对象时,JVM 都会创建

  1. 堆中的一个对象
  2. SCP 中的一个字符串文字对象,如果它不存在的话

并且heap中的对象在内部指向SCP中的文字对象

并且每次我们在 s2 或任何其他字符串中进行更改(不管它是从文字创建还是使用 new)都会创建一个新的字符串文字堆,新更改的 s2 将指向该堆。

使用 String s2 = new String("hello") 不会在堆中创建 "hello" 对象。仅当 SCP 不存在且 s2 指向它时,JVM 才会在 SCP 中创建 "hello"

我的问题不是,new String("hello")和简单的"hello"有什么区别。

我的问题是使用 public String(String original) 只是在堆中创建空字符串对象并浪费内存 为什么 Java 允许开发人员调用 public String(String original) 以及为什么它甚至在 String class,它有什么好处?

Joshua Bloch 的“Effective Java”,第 2 版,第 4 章,第 15 项中有一个有趣的陈述:

A consequence of the fact that immutable objects can be shared freely is that you never have to make defensive copies (Item 39). In fact, you never have to make any copies at all because the copies would be forever equivalent to the originals. Therefore, you need not and should not provide a clone method or copy constructor (Item 11) on an immutable class. This was not well understood in the early days of the Java platform, so the String class does have a copy constructor, but it should rarely, if ever, be used (Item 5).

(我的第76页)
我认为,Joshua Bloch 可以被视为权威来源,尤其是 Java 发明家之一詹姆斯·高斯林 (James Gosling) 曾被引用说:“我真希望我十年前就有这本书……”(指2001 年第 1 版)。


所以 String(String) 构造函数的存在可以看作是一个设计错误,就像无参数 String() 构造函数一样。还要注意工厂方法 String.valueOf(char[])/ String.valueOf(char[],int,int) and String.copyValueOf(char[])/ String.copyValueOf(char[],int,int) 的存在,其命名暗示了根本不存在的根本区别。 String 的不可变性质要求所有变体创建所提供数组的防御副本,以防止后续修改。因此,无论您使用 valueOf 还是 copyValueOf.

,行为都完全相同(文档明确说明)

也就是说,有一些实际用例,但不一定符合初衷。 this question 的答案中描述了其中一些。由于 new 操作保证产生一个新实例,它可能对依赖于不同标识的任何后续操作有用,例如在该实例上同步(这不是一个好主意)或尝试通过身份比较来识别该实例以确保它不是来自外部来源。例如,您可能想要区分 属性 的默认值和已明确设置的值。然而,这是有限的用途,因为其他代码可能无法保证在其操作中保持对象身份,即使字符串内容没有改变。或者它可能会记住您的特殊实例并在遇到字符串后重新使用它。

在 Java7 之前,更新 6,String 有一个 offsetlength 字段,允许便宜的 substring 操作,指的是一个范围在原始数组中,无需复制。这导致了这样一种情况,即一个(概念上的)小字符串可以包含对一个相当大的数组的引用,从而阻止其垃圾收集。对于参考实现(由 Sun/later Oracle 提供),通过 String(String) 构造函数重新创建字符串生成了一个 String 和数组的新副本,仅占用所需的内存。所以这是一个用例,包含针对特定于实现的问题的特定于实现的修复……

当前 Java 版本不维护这些 offsetlength 字段,这意味着可能更昂贵的 substring 操作,但 [=12] 中没有复制行为=] 构造函数了。这是您在问题中引用其源代码的版本。可以在 this answer.

中找到旧版本