在查看在线代码示例时,有时会遇到通过使用new运算符将String常量分配给String对象的情况。
例如:
String s;
...
s = new String("Hello World");
当然,与
s = "Hello World";
我不熟悉这种语法,也不知道其目的或效果。由于String常量通常存储在常量池中,然后JVM以任何表示形式存储在处理String常量中,因此什至会在堆上分配任何东西吗?
在查看在线代码示例时,有时会遇到通过使用new运算符将String常量分配给String对象的情况。
例如:
String s;
...
s = new String("Hello World");
当然,与
s = "Hello World";
我不熟悉这种语法,也不知道其目的或效果。由于String常量通常存储在常量池中,然后JVM以任何表示形式存储在处理String常量中,因此什至会在堆上分配任何东西吗?
new String(String)使用的答案,s = new String("Hello World")参数是文字的情况在Java中都没有意义,而且可能永远不会。
Answers:
在一个地方,你可能会认为你想new String(String)是迫使内部字符数组的一个独特的副本,如
small=new String(huge.substring(10,20))
但是,不幸的是,此行为是未记录的,并且取决于实现。
当将大文件(某些文件最多可达20 MiB)读入字符串并在事后将其刻成几行时,我对此感到非常恼火。我最后得到了引用由整个文件组成的char []的所有字符串。不幸的是,与处理文件相比,我无意间保留了对整个数组的引用,而保留的时间却比处理文件更长new String()。由于处理20,000个文件很快就消耗了大量的RAM,所以我不得不使用它来解决该问题。
唯一与实现无关的方法是:
small=new String(huge.substring(10,20).toCharArray());
不幸的是,这必须将数组复制两次,一次toCharArray()并在String构造函数中一次。
需要有一种通过复制现有字符串的字符来获取新String的文档化方法;或String(String)需要改进的文档以使其更明确(此处有隐含含义,但它相当模糊且易于解释)。
为了回应不断出现的评论,请观察Apache Harmony的实现new String()是:
public String(String string) {
value = string.value;
offset = string.offset;
count = string.count;
}
没错,那里没有底层数组的副本。但是,它仍然符合(Java 7)String文档,因为它:
初始化一个新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要显式的原始副本,否则不需要使用此构造函数,因为字符串是不可变的。
突出的部分是“参数字符串的副本”;它不会说“参数字符串和支持该字符串的基础字符数组的副本”。
请注意,您应按照文档而不是一种 实现编程。
String(String)构造函数的JavaDoc说:“初始化新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要显式的原始副本,否则,由于字符串是不可变的,因此不需要使用此构造函数。”这是一种回旋方式,可以说所述构造函数char[]对String传递给它的底层进行了显式复制。
String(char[] value)其与明确地指出:进行比较:复制字符数组的内容。
!explicitCopyRequired --> constructor is unnecessary(->含义为'隐含')和规则a --> b<==>!b --> !a来揭示constructor is necessary --> explicitCopyRequired。如果使用构造函数,那是因为您需要显式副本。现在,我同意用更好的措词,但是当您分解它时,很显然此构造函数通过合同创建了一个显式副本。
new String(String)而没有复制基础字符数组-可能是Apache Harmony,但我不确定。
我唯一发现这有用的是声明锁变量:
private final String lock = new String("Database lock");
....
synchronized(lock)
{
// do something
}
在这种情况下,诸如Eclipse之类的调试工具将在列出线程当前持有或正在等待的锁定时显示该字符串。您必须使用“新字符串”,即分配一个新的字符串对象,因为否则共享字符串文字可能会锁定在其他一些不相关的代码中。
private static class Lock {}; private final Lock lock = new Lock();,因为类名几乎随处可见。但是,由于HotSPot效率不高,因此将花费您数K的费用。
Serializable,所以我更喜欢new Object[0]。
Software Monkey和Ruggs描述的该构造函数的唯一实用工具似乎已从JDK7中消失。offset类String中不再有字段,并且子字符串始终使用
Arrays.copyOfRange(char[] original, int from, int to)
修剪副本的char数组。
substring()并不意味着它必须在每个实现中都必须这样做(而且OpenJDK并不是唯一的JVM实现)。
字符串s1 =“ foo”; 文字将放在StringPool中,而s1将被引用。
字符串s2 =“ foo”; 这次它将检查StringPool中是否已经有“ foo”文字,因为它现在已经存在,所以s2将引用相同的文字。
字符串s3 = new字符串(“ foo”); 首先在StringPool中创建“ foo”文字,然后通过字符串arg构造函数创建String Object,即由于通过新运算符创建对象而在堆中创建“ foo”,然后s3将引用它。
字符串s4 = new字符串(“ foo”); 与s3相同
所以System.out.println(s1 == s2); //由于文字比较而为true。
和System.out.println(s3 == s4); //由于对象比较而为false(在堆中的不同位置创建了s3和s4)
好吧,这取决于示例中的“ ...”。例如,如果它是一个StringBuffer或一个字节数组或其他东西,您将获得一个由传递的数据构造的String。
但是,如果它只是另一个String,如中所示new String("Hello World!"),则"Hello World!"在所有情况下都应将其替换为simple 。字符串是不可变的,因此克隆一个字符串毫无用处-创建一个新的String对象只是作为现有String的副本(无论是文字变量还是您已经拥有的另一个String变量),它的冗长且效率较低。
实际上,Effective Java(我强烈建议)将其完全用作其“避免创建不必要的对象”的示例之一:
作为不执行操作的极端示例,请考虑以下语句:
String s = new String("stringette"); **//DON'T DO THIS!**
(有效的Java,第二版)
以下是《有效的Java第三版》(第17项:最小化可变性)一书的引文:
可以自由共享不可变对象的事实的结果是,您不必为它们制作防御性副本(第50项)。实际上,您根本不必制作任何副本,因为这些副本将永远等同于原始副本。因此,您不需要也不应该在不可变类上提供克隆方法或副本构造函数(第13项)。在Java平台的早期,这还没有得到很好的理解,因此String类确实具有复制构造函数,但是很少使用(如果有的话)。
所以,这是由Java的一个错误的决定,因为String类是不可变的,他们不应该提供拷贝构造函数这个类中,你想要做的不变类昂贵的操作情况下,你可以使用的是公共可变伴侣类StringBuilder和StringBuffer在情况String。
通常,这表明有人对初始化时声明的新型C ++样式不满意。
早在C天之前,在内部范围内定义自动变量并不是一种好方法。C ++消除了解析器限制,Java对此进行了扩展。
所以你看到的代码有
int q;
for(q=0;q<MAX;q++){
String s;
int ix;
// other stuff
s = new String("Hello, there!");
// do something with s
}
在极端情况下,所有声明都可能在函数的顶部,而不是在for此处的循环之类的封闭范围内。
但是,总的来说,这样做的结果是导致String ctor被调用一次,并丢弃生成的String。(避免这种情况的愿望正是导致Stroustrup允许在代码中的任何位置进行声明的原因。)因此,您是正确的,它充其量是不必要的,不好的样式,甚至可能实际上是不好的。
用Java创建字符串的方式有两种。以下是这两种方法的示例:1)声明类型为String(Java中的类)的变量,并将其分配给应放在双引号之间的值。这将在内存的字符串池区域中创建一个字符串。例如:String str =“ JAVA”;
2)使用String类的构造函数并传递一个字符串(双引号内)作为参数。例如:String s = new String(“ JAVA”); 这将在主内存中以及在字符串池中创建一个新的字符串JAVA(如果该字符串尚未出现在字符串池中)。
substring,trim,replace,replaceAll,StringBuilder,StringBuffer,等
我想这将取决于您看到的代码示例。
在代码示例中,大多数时候使用类构造函数“ new String()”只是为了显示一个众所周知的java类,而不是创建一个新的java类。
您应该避免大多数时候使用它。不仅因为字符串文字是intern,而且主要是因为字符串是不可变的。具有代表同一对象的两个副本没有任何意义。
尽管Ruggs提到的该文章“很有趣”,但除非有非常特殊的情况,否则不应使用该文章,因为它可能造成大于实际的损害。您将编码为实现而不是规范,并且相同的代码在JRockit,IBM VM或其他产品中无法运行相同的代码。