在Java中通过引用传递字符串?
我习惯于在在Java中通过引用传递字符串?,java,string,pass-by-reference,Java,String,Pass By Reference,我习惯于在C中执行以下操作: void main() { String zText = ""; fillString(zText); printf(zText); } void fillString(String zText) { zText += "foo"; } 输出为: foo 然而,在Java中,这似乎不起作用。我假设是因为字符串对象是复制的,而不是通过引用传递的。我认为字符串是对象,总是通过引用传递 这里发生了什么?java.lang.String
C
中执行以下操作:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
输出为:
foo
然而,在Java中,这似乎不起作用。我假设是因为字符串
对象是复制的,而不是通过引用传递的。我认为字符串是对象,总是通过引用传递
这里发生了什么?java.lang.String是不可变的
我讨厌粘贴URL,但如果你在java世界,这对你阅读和理解是必不可少的。字符串是java语言 您有三种选择:
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通常是最好的选择。在Java中,没有任何东西是通过引用传递的。一切都是通过值传递的。对象引用是按值传递的。此外,字符串是不可变的。因此,当您附加到传递的字符串时,您只会得到一个新字符串。您可以使用返回值,也可以传递StringBuffer。发生的情况是,引用按值传递,即传递引用的副本。java中没有任何内容是通过引用传递的,而且由于字符串是不可变的,因此该赋值创建了一个新的字符串对象,引用的副本现在指向该对象。原始引用仍然指向空字符串
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);
}
}
这对于任何对象都是一样的,即在方法中将其设置为新值。下面的例子只是让事情变得更加明显,但是连接字符串实际上是一样的
void foo( object o )
{
o = new Object( ); // original reference still points to old value on the heap
}
Java中的所有参数都是按值传递的。将
字符串
传递给函数时,传递的值是对字符串对象的引用,但不能修改该引用,并且基础字符串对象是不可变的
任务
zText += foo;
相当于:
zText = new String(zText + "foo");
也就是说,它(本地)将参数zText
重新指定为一个新的引用,该引用指向一个新的内存位置,其中是一个新的字符串
,该字符串包含zText
的原始内容,并附加了“foo”
原始对象未被修改,main()
方法的局部变量zText
仍然指向原始(空)字符串
印刷品:
Original value:
Local value: foo
Final value:
Original value:
Local value: foo
Final value: foo
如果要修改字符串,可以使用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:
Original value:
Local value: foo
Final value: foo
在一般情况下,这可能是最类似Java的解决方案——请参阅有效的Java项目“Favor immutability”
不过,如前所述,StringBuilder
通常会给您带来更好的性能——如果您有很多附加工作要做,特别是在循环中,请使用StringBuilder
但是如果可以的话,尝试传递不可变的
字符串
而不是可变的StringBuilders
——您的代码将更易于阅读和维护。考虑将参数“代码>最终< /COD>”,并在将参数重新分配给新值时配置IDE警告您。字符串是java中的一个特殊类。它是线程安全的,这意味着“一旦创建了字符串实例,字符串实例的内容将永远不会更改”
这就是发生的事情 zText += "foo";
首先,Java编译器将获取zText字符串实例的值,然后创建一个新的字符串实例,该实例的值为zText并附加“foo”。所以您知道为什么zText指向的实例没有更改。这是一个全新的例子。事实上,甚至字符串“foo”也是一个新的字符串实例。因此,对于该语句,Java将创建两个字符串实例,一个是“foo”,另一个是zText append“foo”的值。
规则很简单:字符串实例的值永远不会更改
对于方法fillString,您可以使用StringBuffer作为参数,也可以如下更改:
String fillString(String zText) {
return zText += "foo";
}
对象通过引用传递,基本体通过值传递 字符串不是一个原语,它是一个对象,它是对象的一个特例 这是为了节省内存。在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正在检查内容(值)这项工作使用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");
}
}
更好地使用StringBuilder
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");
}
}
字符串在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");
}
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";
}
另一个选项是创建一个以字符串作为作用域变量的类(非常不推荐),如下所示:
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";
}
答案很简单。在java中,字符串是不可变的。因此,它类似于使用“final”修饰符(或C/C++中的“const”)。因此,一旦分配,您就不能像以前那样更改它 可以更改字符串指向的值,但不能更改该字符串当前指向的实际值 例如,
String s1=“嘿”
。您可以将s1=“woah”
,这是完全可以的,但实际上,您不能将字符串的基本值(在本例中为“hey”)更改为使用plusEquals等赋值后的其他值(即s1+=“whatup!=“hey whatup”
)
为此,请使用StringBuilder或StringBuffer类或其他可变容器,然后调用.toString()将对象转换回字符串
注意:字符串通常用作散列键,因此这是它们不可变的部分原因。Aaron Digulla给出了迄今为止最好的答案。他的第二个选项的一个变体是使用公共对象的包装器或容器类MutableObject