在Java中通过引用传递string?
我习惯于在C
:
void main() { String zText = ""; fillString(zText); printf(zText); } void fillString(String zText) { zText += "foo"; }
输出是:
foo
但是,在Java中,这似乎不起作用。 我假设,因为String
对象被复制,而不是通过引用传递 。 我认为string是对象,它总是通过引用传递。
这里发生了什么?
你有三个select:
-
使用StringBuilder:
StringBuilder zText = new StringBuilder (); void fillString(StringBuilder zText) { zText.append ("foo"); }
-
创build一个容器类,并将容器的一个实例传递给你的方法:
public class Container { public String data; } void fillString(Container c) { c.data += "foo"; }
-
创build一个数组:
new String[] zText = new String[1]; zText[0] = ""; void fillString(String[] zText) { zText[0] += "foo"; }
从性能angular度来看,StringBuilder通常是最好的select。
在Java中没有任何东西通过引用传递 。 一切都通过价值传递 。 对象引用是按值传递的。 另外string是不可变的 。 所以当你附加到传递的string,你只是得到一个新的string。 你可以使用返回值,或者传递一个StringBuffer。
正在发生的事情是,引用是通过值传递的,即通过引用的副本 。 java中没有任何内容是通过引用传递的,由于一个string是不可变的,所以这个赋值创build了一个新的string对象, 引用的拷贝现在指向这个对象。 原始引用仍然指向空string。
这对于任何对象都是一样的,即在方法中将其设置为新的值。 下面的例子让事情变得更加明显,但是连接一个string真的是一回事。
void foo( object o ) { o = new Object( ); // original reference still points to old value on the heap }
java.lang.String是不可变的。
我讨厌粘贴url,但是http://java.sun.com/javase/6/docs/api/java/lang/String.html对于您阅读和理解您是否在java-land中至关重要;
对象通过引用传递,原语通过值传递。
string不是一个原语,它是一个对象,它是对象的特例。
这是为了节省内存的目的。 在JVM中,有一个string池。 对于创build的每个string,JVM都会尝试查看string池中是否存在相同的string,如果已经存在,则指向该string。
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正在检查内容(值)
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"); }
另一个select是创build一个String类作为范围variables(非常不鼓励),如下所示:
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通过引用传递(严格来说,正如Ed Swangren所指出的那样,对string的引用是按值传递的),但它们是不可变的。
zText += foo;
相当于
zText = new String(zText + "foo");
也就是说,它修改(局部)variableszText
,使其现在指向一个新的内存位置,其中是一个带有zText
原始内容的zText
,附加了"foo"
。 原始对象未被修改,而main()
方法的局部variableszText
仍指向原始(空)string。
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:
如果你想修改string,你可以使用StringBuilder
或其他一些容器(一个数组或一个自定义的容器类)来提供额外的指针间接级别。 或者,只需返回新的值并赋值:
class StringFiller2 { static StringfillString(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的解决scheme – 请参阅有效的Java项目“有利于不变性”。
如前所述, StringBuilder
经常会给你更好的性能 – 如果你有很多的附加工作,特别是在循环内部,使用StringBuilder
。
但是,如果可以的话,尝试传递不可变的Strings
而不是可变的StringBuilders
– 你的代码将更容易阅读,更易于维护。 考虑将参数设置为final
,并将您的IDEconfiguration为在将方法参数重新分配给新值时发出警告。
答案很简单。 在Java中,string是不可变的。 因此它就像使用'final'修饰符(或C / C ++中的'const')。 所以,一旦分配,你不能像你所做的那样改变它。
您可以更改string指向的值,但不能更改该string当前指向的实际值。
IE浏览器。 String s1 = "hey"
。 你可以使s1 = "woah"
,这是完全没问题的,但是你实际上不能改变string的底层值(在这个例子中是“hey”),当它使用plusEquals等赋值时s1 += " whatup != "hey whatup"
)。
为此,使用StringBuilder或StringBuffer类或其他可变容器,然后调用.toString()将对象转换回string。
注意:string经常用作散列键,因此这是它们不可变的原因之一。
string在Java中是不可变的。
string是Java中的一个特殊类。 这是线程保存,意思是“一旦创build了一个String实例,String实例的内容将永远不会改变”。
这是发生了什么事情
zText += "foo";
首先,Java编译器将获得zTextstring实例的值,然后创build一个新的string实例,其值为zText,附加“foo”。 所以你知道为什么zText指向的实例没有改变。 这完全是一个新的例子。 实际上,即使是String“foo”也是一个新的String实例。 因此,对于这个语句,Java将创build两个String实例,一个是“foo”,另一个是zText的值附加“foo”。 规则很简单:String实例的值永远不会改变。
对于方法fillString,你可以使用一个StringBuffer作为参数,或者你可以像这样改变它:
String fillString(String zText) { return zText += "foo"; }
这工作使用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"); } }
string在Java中是不可变的。 您不能修改/更改现有的string文字/对象。
String s =“Hello”; S = S + “喜”;
在这里,前面的引用被新的引用指向值“HelloHi”所取代。
但是,为了带来可变性,我们有StringBuilder和StringBuffer。
StringBuilder s = new StringBuilder(); s.append( “你好”);
这会将新值“Hi”添加到相同的引用中。 //
Aaron Digulla到目前为止是最好的答案。 他的第二个select的变体是使用commons lang库版本3+的包装器或容器类MutableObject:
void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }
您保存容器类的声明。 缺点是对公共资源库的依赖。 但是lib有相当多的有用的function,几乎所有我曾经使用过的大型项目都使用它。