在查看在线代码示例时,我有时会通过使用new运算符将String常量赋值给String对象。
例如:
String s;
...
s = new String("Hello World");
这当然与
相比s = "Hello World";
我不熟悉这种语法,也不知道目的或效果是什么。 由于String常量通常存储在常量池中,然后以JVM用于处理String常量的任何表示形式,甚至可以在堆上分配任何内容吗?
答案 0 :(得分:71)
您可能需要new String(String)
的地方是强制子字符串复制到新的基础字符数组,如
small=new String(huge.substring(10,20))
但是,遗憾的是,这种行为没有记录,并且依赖于实现。
当我将大文件(一些高达20 MiB)读入一个字符串并在事后将其雕刻成行后,我就被这个烧了。我最后得到了引用由整个文件组成的char []的行的所有字符串。不幸的是,无意中保留了对整个数组的引用,因为我所持有的几行比处理文件的时间更长 - 我被迫使用new String()
来解决它。
唯一与实现无关的方法是:
small=new String(huge.substring(10,20).toCharArray());
遗憾的是,这必须复制数组两次,一次是toCharArray()
,一次是在String构造函数中。
需要有一种记录的方法来通过复制现有字符串来获取新的字符串;或者String(String)
的文档需要进一步改进以使其更加明确(这里有一个含义,但它的解释相当模糊和开放)。
在回应不断发表的评论时,请观察new String()
的Apache Harmony实现:
public String(String string) {
value = string.value;
offset = string.offset;
count = string.count;
}
没错,那里没有底层数组的副本。然而,它仍然符合(Java 7)String文档,因为它:
初始化一个新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要显式的原始副本,否则不必使用此构造函数,因为字符串是不可变的。
显着的部分是“参数 string ”的副本;它没有说“参数字符串的副本和支持字符串的基础字符数组”。
请注意编程文档而不是一个 实现。
答案 1 :(得分:7)
我发现这个有用的唯一一次是声明锁定变量:
private final String lock = new String("Database lock");
....
synchronized(lock)
{
// do something
}
在这种情况下,Eclipse等调试工具会在列出当前持有或正在等待的线程锁定时显示该字符串。您必须使用“new String”,即分配一个新的String对象,否则共享字符串文字可能会被锁定在其他一些不相关的代码中。
答案 2 :(得分:4)
字符串s1 =“foo”; 字面值将在StringPool中,s1将引用。
字符串s2 =“foo”; 这次它会检查“foo”文字在StringPool中是否已经可用,因为它现在存在,所以s2将引用相同的文字。
String s3 = new String(“foo”); “foo”literal将首先在StringPool中创建,然后通过字符串arg构造函数String对象将被创建,即堆中的“foo”由于通过new运算符创建对象,然后s3将引用它。
String s4 = new String(“foo”); 与s3相同
所以 System.out.println(s1 == s2); // true 由于字面比较。
由于对象比较,和 System.out.println(s3 == s4); // false (s3和s4在堆中的不同位置创建)< / p>
答案 3 :(得分:2)
Software Monkey和Ruggs描述的这个构造函数的唯一实用程序似乎已经从JDK7中消失了。
类String中不再有offset
字段,子字符串总是使用
Arrays.copyOfRange(char[] original, int from, int to)
修剪复制的char数组。
答案 4 :(得分:1)
嗯,这取决于示例中的“......”。例如,如果它是一个StringBuffer,或者是一个字节数组,那么你将获得一个根据你传递的数据构造的String。
但是如果它只是另一个字符串,就像在new String("Hello World!")
中一样,那么它应该在所有情况下都被"Hello World!"
替换。字符串是不可变的,因此克隆字符串没有用处 - 创建一个新的String对象只是作为现有字符串的副本(无论是文字还是已有的另一个字符串变量),它更加冗长,效率更低。
事实上,Effective Java(我强烈推荐)使用它作为“避免创建不必要的对象”的例子之一:
作为不做的极端例子,请考虑以下陈述:
String s = new String("stringette"); **//DON'T DO THIS!**
(Effective Java,Second Edition)
答案 5 :(得分:0)
以下是《有效的Java第三版》(第17项:最小化可变性)一书中的引文:
不可变对象可以自由共享这一事实的结果 就是您不必为它们制作防御性副本(项目 50)。实际上,您根本不需要复制任何副本,因为 副本将永远等同于原件。因此,你 不需要,也不应提供克隆方法或副本构造函数 (第13项)不可变的类别。这在 Java平台的早期,所以String类确实有一个副本 构造函数,但很少(如果有的话)使用。
因此,这是Java的错误决定,因为Write_back_into_file(name)
类是不可变的,因此不应为该类提供副本构造函数,如果您要对不可变类进行昂贵的操作,则可以使用公共可变伴侣在String
的情况下为StringBuilder
和StringBuffer
的类。
答案 6 :(得分:-1)
通常,这表示有人对初始化时声明的新式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在代码中的任何地方允许声明的原因。)所以你是正确的,它是最好的不必要和坏的风格,可能实际上是坏的。
答案 7 :(得分:-1)
我想这取决于您所看到的代码示例。
大多数时候在代码示例中使用类构造函数“new String()”只是为了显示一个非常熟悉的java类而不是创建一个新类。
你应该避免在大多数时候使用它。不仅因为字符串文字被中断,而且主要是因为字符串是不可变的。有两个副本代表同一个对象是没有意义的。
虽然Ruggs所引用的文章“有趣”,但除非非常具体,否则不应该使用它,因为它可能造成更多的伤害而不是好处。您将编写实现而不是规范,并且相同的代码无法在JRockit,IBM VM或其他代码中运行相同的代码。
答案 8 :(得分:-1)
有两种方法可以在Java中创建字符串。以下是两种方式的示例: 1)声明String类型的变量(Java中的类)并将其赋值给应放在双引号之间的值。这将在内存的字符串池区域中创建一个字符串。 例如:String str =“JAVA”;
2)使用String类的构造函数并传递一个字符串(在双引号内)作为参数。 例如:String s = new String(“JAVA”); 这将在主内存中创建一个新的字符串JAVA,如果字符串池中不存在该字符串,则会在字符串池中创建。