Sor*_*son 150 java string pass-by-reference
我习惯于在以下方面做以下事情C:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
Run Code Online (Sandbox Code Playgroud)
输出是:
foo
Run Code Online (Sandbox Code Playgroud)
但是,在Java中,这似乎不起作用.我假设因为String对象被复制而不是被引用传递.我认为字符串是对象,它总是通过引用传递.
这里发生了什么?
Aar*_*lla 189
你有三个选择:
使用StringBuilder:
StringBuilder zText = new StringBuilder ();
void fillString(StringBuilder zText) { zText.append ("foo"); }
Run Code Online (Sandbox Code Playgroud)创建容器类并将容器的实例传递给您的方法:
public class Container { public String data; }
void fillString(Container c) { c.data += "foo"; }
Run Code Online (Sandbox Code Playgroud)创建一个数组:
new String[] zText = new String[1];
zText[0] = "";
void fillString(String[] zText) { zText[0] += "foo"; }
Run Code Online (Sandbox Code Playgroud)从性能的角度来看,StringBuilder通常是最佳选择.
zed*_*doo 82
在Java中没有任何内容通过引用传递.一切都是通过价值传递的.对象引用按值传递.另外字符串是不可变的.因此,当您附加到传递的String时,您只需获得一个新的String.您可以使用返回值,或者传递StringBuffer.
Ed *_* S. 50
发生的事情是引用按值传递,即传递引用的副本.java中没有任何内容通过引用传递,并且由于字符串是不可变的,因此该赋值创建了一个新的字符串对象,该引用的副本现在指向该对象.原始引用仍指向空字符串.
这对任何对象都是相同的,即在方法中将其设置为新值.下面的例子只是让事情变得更加明显,但连接一个字符串实际上是一回事.
void foo( object o )
{
o = new Object( ); // original reference still points to old value on the heap
}
Run Code Online (Sandbox Code Playgroud)
Rya*_*des 18
java.lang.String是不可变的.
我讨厌粘贴URL,但https://docs.oracle.com/javase/10/docs/api/java/lang/String.html对于您阅读和理解是否在java-land中是必不可少的.
对象通过引用传递,基元通过值传递.
字符串不是基元,它是一个对象,它是对象的特例.
这是为了节省内存.在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));
}
}
Run Code Online (Sandbox Code Playgroud)
/*OUTPUT*/
a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false
Run Code Online (Sandbox Code Playgroud)
==正在检查内存地址(引用),.equals正在检查内容(值)
Java中的所有参数都是按值传递的.将a传递String给函数时,传递的值是对String对象的引用,但您无法修改该引用,并且基础String对象是不可变的.
分配
zText += foo;
Run Code Online (Sandbox Code Playgroud)
相当于:
zText = new String(zText + "foo");
Run Code Online (Sandbox Code Playgroud)
即,(局部地)重新指定参数zText作为新的参考,它指向一个新的存储位置,其中,是一种新的String,它包含的原始内容zText与"foo"追加.
原始对象未被修改,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);
}
}
Run Code Online (Sandbox Code Playgroud)
打印:
Original value:
Local value: foo
Final value:
Run Code Online (Sandbox Code Playgroud)
如果你想修改字符串,你可以使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
打印:
Original value:
Local value: foo
Final value: foo
Run Code Online (Sandbox Code Playgroud)
这可能是一般情况下最类似Java的解决方案 - 请参阅Effective Java项目"Favor immutability".
但是,如前所述,StringBuilder通常会为您提供更好的性能 - 如果您有很多需要做的事情,特别是在循环内部,请使用StringBuilder.
但是如果可以的话,尽量传递不可变的Strings而不是可变的StringBuilders- 你的代码将更容易阅读和更易于维护.考虑制作参数final,并配置IDE以在将方法参数重新分配给新值时发出警告.
String是Java中的不可变对象.您可以使用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");
}
Run Code Online (Sandbox Code Playgroud)
另一个选择是创建一个类,其中String作为范围变量(非常不鼓励),如下所示:
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";
}
Run Code Online (Sandbox Code Playgroud)