在Java中通过引用传递string?

我习惯于在C

 void main() { String zText = ""; fillString(zText); printf(zText); } void fillString(String zText) { zText += "foo"; } 

输出是:

 foo 

但是,在Java中,这似乎不起作用。 我假设,因为String对象被复制,而不是通过引用传递 。 我认为string是对象,它总是通过引用传递。

这里发生了什么?

你有三个select:

  1. 使用StringBuilder:

     StringBuilder zText = new StringBuilder (); void fillString(StringBuilder zText) { zText.append ("foo"); } 
  2. 创build一个容器类,并将容器的一个实例传递给你的方法:

     public class Container { public String data; } void fillString(Container c) { c.data += "foo"; } 
  3. 创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 } 

对象通过引用传递,原语通过值传递。

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,几乎所有我曾经使用过的大型项目都使用它。