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

标签 java string string-literals

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

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

当我们说

String s2 = new String("hello");

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

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

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

  1. 堆中的一个对象以及
  2. SCP 中的一个字符串文字对象(如果它尚不存在)

堆中的对象内部指向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 类中提供,它有什么好处?

最佳答案

Joshua Bloch 的《Effective Java》,第二版,第 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 年第一版) )。

<小时/>

因此,String(String) 构造函数的存在可以被视为一个设计错误,就像无参数 String() 构造函数一样。另请注意工厂方法的存在 String.valueOf(char[])/String.valueOf(char[],int,int)String.copyValueOf(char[])/String.copyValueOf(char[],int,int) ,其命名暗示了根本不存在的根本区别。 String 的不可变性质要求所有变体都创建所提供数组的防御性副本,以防止后续修改。因此,无论您使用 valueOf 还是 copyValueOf,行为都是完全相同的(文档明确说明了这一点)。

<小时/>

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

在 Java 7、更新 6 之前,String 有一个 offsetlength 字段,允许使用廉价的子字符串操作,引用原始数组内的范围,而不进行复制。这导致了这样的情况:(概念上)小字符串可以保存对相当大数组的引用,从而阻止其垃圾收集。对于引用实现(由 Sun/后来的 Oracle 提供),通过 String(String) 构造函数重新创建字符串会生成一个带有数组新副本的 String,占用仅需要所需的内存。所以这是一个用例,结合了针对特定于实现的问题的特定于实现的修复......

当前的 Java 版本不维护这些 offsetlength 字段,这意味着可能更昂贵的 substring 操作,但在不再是 String(String) 构造函数了。这是您在问题中引用的版本,其源代码。旧版本可以在 this answer 中找到。 .

关于java - 当它间接指向 ("hello"时,使用 new String "hello") 是否比简单的 "hello"完全无用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40721145/

相关文章:

ruby - 在 ruby​​ 中解析一个非常特殊的字符串结构(棒球数据)

c - long string literal 的类型是 long int* 吗?

javascript - 当我不知道它们的位置时,如何从 JavaScript 中的较大字符串中删除一些字符串

java - 为什么我无法在 Java 中访问这个创建的对象?

java - 如何将 BufferedReader 写入 slf4j 记录器

java - 在 Java 中,如何将 byte[] 正确地转换为 String 再到 byte[]?

java - 如何在 java 中创建具有枚举值的 map ?

java - 创建新字符串作为文字/新对象时的内存分配

高于 U+FFFF 的字符的 Javascript 文字

java - 使用 Jsoup 和 Android Studio 截断网站