问题描述
执行 String S1 ="hello";
之后,JVM将在SCP中创建一个String对象,并且该对象将在 value
字段中保存一个char数组,例如
After executing String S1 = "hello";
JVM will create a String object in SCP and that object will hold an array of char in value
field like
s1.value = {'h', 'e', 'l', 'l', 'o'}
当我们说
String s2 = new String("hello");
根据构造函数执行后 String
类的源代码, s2.value
也将成为"hello" .value
类似于 s1.value
.
And according to the source code of String
class after constructor execution s2.value
will also become "hello".value
which will be similar to s1.value
.
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
因此,每次我们使用 new
JVM创建String对象时,都会创建
So every time we create String object using new
JVM will create
- 堆中的一个对象和
- SCP中的一个字符串文字对象(如果尚未存在)
堆中的对象内部指向SCP中的文字对象.
And the object in heap points to the literal object in SCP internally.
每次,我们都会在 s2
或任何其他字符串(无论是从文字中创建还是使用 new
进行更改)中更改一个新的字符串文字将在堆上创建,新更改的 s2
将指向该堆.
And every time, we make a change in s2
or in any other string (doesn't matter it is created from literal or using new
) one new string literal will get created on the heap, which that newly changed s2
will point.
使用 String s2 = new String("hello")
未在堆中创建"hello"
对象.JVM仅在SCP中不存在且s2指向它的情况下才在SCP中创建"hello"
.
Using String s2 = new String("hello")
is not creating "hello"
object in heap. JVM is creating "hello"
in SCP only if it is not present there and s2 pointing to it.
我的问题不是, new String("hello")
或简单的"hello"
有什么区别.
My question is not, what is the difference between new String("hello")
or simple "hello"
.
我的问题是,当使用 public String(String original)
只是在堆中创建空字符串对象并浪费内存时,为什么Java允许开发人员调用 public String(String original)
为什么甚至在String类中提供了它,它还能带来什么好处?
My question is when using public String(String original)
is just creating empty string object in heap and wasting memory Why Java allows developers to call public String(String original)
and why is it even provided in String class, what benefit it is giving?
推荐答案
约书亚·布洛赫(Joshua Bloch)的有效Java",第二版,第4章,第15项中有一个有趣的陈述:
There is an interesting statement in Joshua Bloch’s "Effective Java", 2nd edition, chapter 4, item 15:
我认为,约书亚·布洛赫(Joshua Bloch)可被视为权威资料,尤其是有人引用Java发明者之一詹姆斯·高斯林(James Gosling)的话说:我希望我十年前有这本书……"(指的是2001年的第一版)).
I think, Joshua Bloch can be seen as an authoritative source, especially as James Gosling, one of the Java inventors, has been cited saying, "I sure wish I had this book ten years ago…" (referring to the 1st edition from 2001).
因此,将 String(String)
构造函数的存在视为设计错误,就像无参数的 String()
构造函数一样.还要注意工厂方法的存在 String.valueOf(char [])
/ String.valueOf(char [],int,int)
和 String.copyValueOf(char []) / String.copyValueOf(char [],int,int)
,其命名暗示了根本不存在的根本区别. String
的不可变性质要求所有变体都创建所提供数组的防御性副本,以防止后续修改.因此,无论您使用 valueOf
还是 copyValueOf
.其行为都是完全相同的(文档中对此进行了明确说明).
So the existence of the String(String)
constructor can be seen as a design mistake, much as the parameterless String()
constructor. Note also the presence of the factory methods String.valueOf(char[])
/ String.valueOf(char[],int,int)
and String.copyValueOf(char[])
/ String.copyValueOf(char[],int,int)
, whose naming suggests a fundamental difference that simply isn’t there. The immutable nature of String
mandates that all variants create a defensive copy of the provided array, to protect against subsequent modifications. So the behavior is exactly the same (the documentation tells this explicitly), whether you use valueOf
or copyValueOf
.
也就是说,有一些实际的用例,尽管不一定在初衷之内.此问题的答案中描述了其中的一些内容.由于 new
操作保证会产生新实例,因此对于依赖于不同身份(例如,在该实例上进行同步(这不是一个好主意),或者尝试通过身份比较来识别该实例,以确保该实例不是来自外部来源.例如,您可能要区分属性的默认值和已明确设置的值.但是,这样做的用途是有限的,因为即使字符串内容没有变化,其他代码也可能无法保证在其操作中保持对象的身份.否则它可能会记住您的特殊实例,并在遇到字符串后再次使用它.
That said, there are some practical use cases, though not necessarily being within original intentions. Some of them are described in the answers to this question. As the new
operation guarantees to produce a new instance, it might be useful for any subsequent operation relying on a distinct identity, e.g. synchronizing on that instance (not that this was a good idea) or trying to recognize that instance via identity comparison to be sure that it doesn’t originate from an external source. E.g., you might want to distinguish between a property’s default value and a value that has been explicitly set. This, however, is of limited use as other code might not guaranty to maintain the object identity in its operations, even if the string contents doesn’t change. Or it might remember your special instance and reuse it, once it encountered the string.
在Java 7,更新6之前, String
具有 offset
和 length
字段,从而允许使用便宜的 substring
操作,指的是原始数组中的一个范围,无需复制.这就导致了这样的情况:一个(概念上)小的字符串可能包含对一个相当大的数组的引用,从而阻止了其垃圾回收.对于参考实现(由Sun/后来的Oracle交付),通过 String(String)
构造函数重新创建字符串会产生一个 String
,其中包含数组的新副本,占用了仅需要的内存.因此,这是一个用例,其中包含针对特定于实现的问题的特定于实现的修复程序……
Before Java 7, update 6, String
had an offset
and length
field, allowing a cheap substring
operation, referring to a range within the original array, without copying. This led to the scenario, that a (conceptually) small string could hold a reference to a rather large array, preventing its garbage collection. For the reference implementation (that shipped by Sun/later Oracle), recreating the string via the String(String)
constructor produced a String
with a fresh copy of the array, occupying only as much memory as needed. So this was a use case incorporating an implementation specific fix to an implementation specific problem…
当前的Java版本不维护这些 offset
和 length
字段,这意味着可能更昂贵的 substring
操作,但是在该版本中没有复制行为. String(String)
构造函数了.这是版本,您在问题中引用了其源代码.可以在此答案中找到较早的版本.
Current Java releases do not maintain these offset
and length
fields, implying a potentially more expensive substring
operation, but no copying behavior in the String(String)
constructor anymore. This is the version, whose source code you have cited in the question. The older version can be found in this answer.
这篇关于当新的String("hello")间接指向"hello"时,它对简单的"hello"完全没有用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!