我习惯在 C
中执行以下操作:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
输出结果为:
foo
然而,在Java中,这似乎并不起作用。我认为这是因为String
对象是复制而不是按引用传递。我以为String
是对象,总是被按引用传递。
这里发生了什么?
我习惯在 C
中执行以下操作:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
输出结果为:
foo
然而,在Java中,这似乎并不起作用。我认为这是因为String
对象是复制而不是按引用传递。我以为String
是对象,总是被按引用传递。
这里发生了什么?
使用 StringBuilder:
StringBuilder zText = new StringBuilder ();
void fillString(StringBuilder zText) { zText.append ("foo"); }
创建一个容器类并将容器的一个实例传递给你的方法: public class Container { public String data; }
void fillString(Container c) { c.data += "foo"; }
创建一个数组:new String[] zText = new String[1];
zText[0] = "";
void fillString(String[] zText) { zText[0] += "foo"; }
从性能角度来看,StringBuilder通常是最好的选择。
StringBuilder
比s += "..."
更快,因为它不会每次都分配新的字符串对象。实际上,当你编写像s = "Hello " + name
这样的Java代码时,编译器将生成字节码,创建一个StringBuilder
并调用append()
两次。 - Aaron Digulla发生的情况是,引用被按值传递,即复制了该引用。在Java中没有任何按引用传递的东西,而且由于字符串是不可变的,所以赋值操作创建了一个新的字符串对象,引用的副本现在指向它。原始引用仍然指向空字符串。
对于任何对象都是这样,即在方法中将其设置为新值。下面的示例只是更明显地说明了正在发生的事情,但连接字符串实际上是相同的操作。
void foo( object o )
{
o = new Object( ); // original reference still points to old value on the heap
}
Java中的java.lang.String是不可变的。
我不喜欢复制URL链接,但如果你在使用Java,那么阅读和理解https://docs.oracle.com/javase/10/docs/api/java/lang/String.html 对你来说至关重要。
String
传递给一个函数时,传递的值实际上是指向一个String对象的引用,但是你不能修改该引用,而且底层的String对象是不可变的。zText += foo;
等同于:
zText = new String(zText + "foo");
也就是说,它(在本地)重新分配参数zText
作为一个新引用,该引用指向一个新的内存位置,在其中包含了原始的zText
内容并附加了"foo"
的新String
。
原始对象没有被修改,main()
方法的本地变量zText
仍然指向原始(空)字符串。
class StringFiller {
static void fillString(String zText) {
zText += "foo";
System.out.println("Local value: " + zText);
}
public static void main(String[] args) {
String zText = "";
System.out.println("Original value: " + zText);
fillString(zText);
System.out.println("Final value: " + zText);
}
}
输出:
Original value:
Local value: foo
Final value:
如果你想修改字符串,可以使用如下所述的 StringBuilder
或者其他容器(例如一个数组、AtomicReference
或自定义的容器类),这些容器提供了额外的指针间接级别。另外,你也可以直接返回新的值并将其赋值:
class StringFiller2 {
static String fillString(String zText) {
zText += "foo";
System.out.println("Local value: " + zText);
return zText;
}
public static void main(String[] args) {
String zText = "";
System.out.println("Original value: " + zText);
zText = fillString(zText);
System.out.println("Final value: " + zText);
}
}
输出:
Original value:
Local value: foo
Final value: foo
这可能是在一般情况下最类似于Java的解决方案——请参见高效Java条目“偏爱不可变性”。
尽管如此,如果你需要大量的追加操作,特别是在循环内部,使用StringBuilder
通常会给你更好的性能。
但如果可以,请尝试传递不可变的Strings
而不是可变的StringBuilders
——你的代码将更易读和更易维护。考虑将参数设置为final
,并配置你的IDE使其在重新分配方法参数到新值时发出警告。
对象是按引用传递的,基本数据类型是按值传递的。
String不是基本数据类型,它是一个对象,也是一种特殊情况的对象。
这是为了节省内存。在JVM中,有一个字符串池。对于每个创建的字符串,JVM都会尝试查看字符串池中是否存在相同的字符串,并在已有字符串的情况下指向它。
public class TestString
{
private static String a = "hello world";
private static String b = "hello world";
private static String c = "hello " + "world";
private static String d = new String("hello world");
private static Object o1 = new Object();
private static Object o2 = new Object();
public static void main(String[] args)
{
System.out.println("a==b:"+(a == b));
System.out.println("a==c:"+(a == c));
System.out.println("a==d:"+(a == d));
System.out.println("a.equals(d):"+(a.equals(d)));
System.out.println("o1==o2:"+(o1 == o2));
passString(a);
passString(d);
}
public static void passString(String s)
{
System.out.println("passString:"+(a == s));
}
}
/* 输出 */
a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false
== 检查内存地址(引用),而 .equals 检查内容(值)。
在Java中,String是一个不可变的对象。您可以使用StringBuilder类来完成您想要完成的工作,具体如下:
public static void main(String[] args)
{
StringBuilder sb = new StringBuilder("hello, world!");
System.out.println(sb);
foo(sb);
System.out.println(sb);
}
public static void foo(StringBuilder str)
{
str.delete(0, str.length());
str.append("String has been modified");
}
class MyString
{
public String value;
}
public static void main(String[] args)
{
MyString ms = new MyString();
ms.value = "Hello, World!";
}
public static void foo(MyString str)
{
str.value = "String has been modified";
}
String是Java中的一个特殊类。它是线程安全的,这意味着“一旦创建了一个String实例,该实例的内容将永远不会改变”。
以下是其背后的原理:
zText += "foo";
String fillString(String zText) {
return zText += "foo";
}
String s1 =“hey”
。您可以使s1 =“woah”
,这是完全可以的,但是您无法实际更改字符串的基础值(在本例中为:“hey”),以便在使用plusEquals等时将其分配给其他内容。 (即s1 + =“whatup!= hey whatup”
)。=
操作符,它永远不会影响原来指向的对象,无论该对象属于哪个类。 - newacct这个可以使用StringBuffer来实现
public class test {
public static void main(String[] args) {
StringBuffer zText = new StringBuffer("");
fillString(zText);
System.out.println(zText.toString());
}
static void fillString(StringBuffer zText) {
zText .append("foo");
}
}
public class test {
public static void main(String[] args) {
StringBuilder zText = new StringBuilder("");
fillString(zText);
System.out.println(zText.toString());
}
static void fillString(StringBuilder zText) {
zText .append("foo");
}
}