Java 中 String(String) 构造函数的使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/465627/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:59:27  来源:igfitidea点击:

Use of the String(String) constructor in Java

javastringcoding-style

提问by Ryan Thames

I've read on articles and books that the use of String s = new String("...");should be avoided pretty much all the time. I understand why that is, but is there any use for using the String(String) constructor whatsoever? I don't think there is, and don't see any evidence otherwise, but I'm wondering if anyone in the SO commmunity knows of a use.

我读过的文章和书籍String s = new String("...");几乎一直都应该避免使用。我明白为什么会这样,但是使用 String(String) 构造函数有什么用处吗?我认为没有,也没有看到任何其他证据,但我想知道 SO 社区中是否有人知道使用。

采纳答案by mmcdole

This is a good article: String constructor considered useless turns out to be useful after all!

这是一篇好文章:被认为无用的字符串构造函数终究是有用的!

It turns out that this constructor can actually be useful in at least one circumstance. If you've ever peeked at the String source code, you'll have seen that it doesn't just have fields for the char array value and the count of characters, but also for the offset to the beginning of the String. This is so that Strings can share the char array value with other Strings, usually results from calling one of the substring() methods. Java was famously chastised for this in jwz' Java rant from years back:

The only reason for this overhead is so that String.substring() can return strings which share the same value array. Doing this at the cost of adding 8 bytes to each and every String object is not a net savings...

Byte savings aside, if you have some code like this:

// imagine a multi-megabyte string here  
String s = "0123456789012345678901234567890123456789";  
String s2 = s.substring(0, 1);  
s = null;

You'll now have a String s2 which, although it seems to be a one-character string, holds a reference to the gigantic char array created in the String s. This means the array won't be garbage collected, even though we've explicitly nulled out the String s!

The fix for this is to use our previously mentioned "useless" String constructor like this:

String s2 = new String(s.substring(0, 1));  

It's not well-known that this constructor actually copies that old contents to a new array if the old array is larger than the count of characters in the string. This means the old String contents will be garbage collected as intended. Happy happy joy joy.

事实证明,这个构造函数至少在一种情况下实际上是有用的。如果您曾经偷看过 String 源代码,您就会发现它不仅有用于 char 数组值和字符数的字段,还有用于到 String 开头的偏移量的字段。这是为了让字符串可以与其他字符串共享 char 数组值,这通常是调用 substring() 方法之一的结果。多年前,在 jwz 的 Java 咆哮中,Java 因这一点受到了著名的惩罚:

这种开销的唯一原因是 String.substring() 可以返回共享相同值数组的字符串。这样做的代价是为每个 String 对象添加 8 个字节,这并不是净节省......

除了字节节省,如果你有这样的代码:

// imagine a multi-megabyte string here  
String s = "0123456789012345678901234567890123456789";  
String s2 = s.substring(0, 1);  
s = null;

您现在将拥有一个 String s2,尽管它看起来是一个单字符的字符串,但它包含对在 String s 中创建的巨大 char 数组的引用。这意味着数组不会被垃圾收集,即使我们已经明确地将 String s 清空了!

解决这个问题的方法是使用我们之前提到的“无用”字符串构造函数,如下所示:

String s2 = new String(s.substring(0, 1));  

如果旧数组大于字符串中的字符数,则该构造函数实际上会将旧内容复制到新数组中,这一点并不为人所知。这意味着旧的 String 内容将按预期进行垃圾收集。快乐快乐快乐快乐。

Finally, Kat Marsenmakes these points,

最后,Kat Marsen提出了这些观点,

First of all, string constants are never garbage collected. Second of all, string constants are intern'd, which means they are shared across the entire VM. This saves memory. But it is not always what you desire.

The copy constructor on String allows you to create a private String instance from a String literal. This can be very valuable for constructing meaningful mutex objects (for the purposes of synchronization).

首先,字符串常量永远不会被垃圾收集。其次,字符串常量是实习的,这意味着它们在整个 VM 中共享。这样可以节省内存。但这并不总是你想要的。

String 上的复制构造函数允许您从 String 文字创建私有 String 实例。这对于构建有意义的互斥对象(出于同步目的)非常有价值。

回答by Martin Thurau

You use it if you need a copyof the original String if you don't want to give a handle to your original string. But since strings are immutable i don't see a use case fot this;)

如果您不想提供原始字符串的句柄,则在需要原始字符串的副本时使用它。但由于字符串是不可变的我没有看到一个用例FOT;)

回答by Douglas Leeder

If I remember correctly the only 'useful' case is where the original String is part of a much larger backing array. (e.g. created as a substring). If you only want to keep the small substring around and garbage collect the large buffer, it might make sense to create a new String.

如果我没记错的话,唯一“有用”的情况是原始字符串是更大的支持数组的一部分。(例如,创建为子字符串)。如果您只想保留小子字符串并对大缓冲区进行垃圾收集,则创建一个新字符串可能是有意义的。

回答by raupach

The constructor is largely redundant and not recommended for general purposes. The String constructor with String argument is rarely used except to create an independent copy of an existing string variable. Basically use it to "clarify" your code. Nothing less.

构造函数在很大程度上是多余的,不推荐用于一般用途。除了创建现有字符串变量的独立副本外,带有 String 参数的 String 构造函数很少使用。基本上用它来“澄清”你的代码。一点也不少。

回答by l_39217_l

from javadoc

来自javadoc

/**
 * Initializes a newly created {@code String} object so that it represents
 * the same sequence of characters as the argument; in other words, the
 * newly created string is a copy of the argument string. Unless an
 * explicit copy of {@code original} is needed, use of this constructor is
 * unnecessary since Strings are immutable.
 *
 * @param  original
 *         A {@code String}
 */
public String(String original){.....}

回答by Jon Skeet

Just to expand on Douglas's answer*, I can give a firm example of where I've used it. Consider reading a dictionary file, with thousands of lines, each one of them just a single word. The simplest way of reading this is to use BufferedReader.readLine().

只是为了扩展道格拉斯的答案*,我可以举一个我在哪里使用它的有力例子。考虑阅读一个包含数千行的字典文件,每行只有一个单词。阅读本文的最简单方法是使用BufferedReader.readLine().

Unfortunately, readLine()allocates a buffer of 80 characters by default as the expected line length. This means that usually it can avoid pointless copying - and a bit of wasted memory isn't normally too bad. However, if you're loading a dictionary of short words in for the duration of the application, you end up with a lot of memory being permanently wasted. My "little app" sucked up far more memory than it should have done.

不幸的是,readLine()默认情况下分配 80 个字符的缓冲区作为预期的行长度。这意味着通常它可以避免无意义的复制 - 浪费一点内存通常不会太糟糕。但是,如果您在应用程序期间加载短词词典,最终会永久浪费大量内存。我的“小应用程序”吸收了比它应该做的更多的内存。

The solution was to change this:

解决方案是改变这一点:

String word = reader.readLine();

into this:

进入这个:

String word = new String(reader.readLine());

... with a comment, of course!

...当然有评论!

*I can't remember whether I was working with Douglas when this actually cropped up, or whether it's just coincidence that he answered this particular question.

*我不记得当这真的突然出现时我是否与道格拉斯一起工作,或者他回答这个特定问题是否只是巧合。