在Java中通过引用传递字符串?
Posted
技术标签:
【中文标题】在Java中通过引用传递字符串?【英文标题】:Passing a String by Reference in Java? 【发布时间】:2010-11-19 05:41:24 【问题描述】:我习惯在C
做以下事情:
void main()
String zText = "";
fillString(zText);
printf(zText);
void fillString(String zText)
zText += "foo";
输出是:
foo
但是,在 Java 中,这似乎不起作用。我假设是因为String
对象是复制,而不是通过引用传递。我认为字符串是对象,总是通过引用传递。
这是怎么回事?
【问题讨论】:
即使它们是通过引用传递的(在 Java 中传递的是引用值的副本,但那是另一个线程)字符串对象是不可变的,所以无论如何都不会工作 这不是 C 代码。 @Phil:这在 C# 中也不起作用。 【参考方案1】:对于好奇的人
class Testt
static void Display(String s , String varname)
System.out.println(varname + " variable data = "+ s + " :: address hashmap = " + s.hashCode());
static void changeto(String s , String t)
System.out.println("entered function");
Display(s , "s");
s = t ;
Display(s,"s");
System.out.println("exiting function");
public static void main(String args[])
String s = "hi" ;
Display(s,"s");
changeto(s,"bye");
Display(s,"s");
现在通过运行上面的代码,您可以看到地址
hashcodes
如何随字符串变量 s 变化。 当 s 改变时,一个新对象被分配给函数changeto
中的变量 s
【讨论】:
【参考方案2】:String 是 Java 中的一个特殊类。它是线程安全的,这意味着“一旦创建了 String 实例,String 实例的内容就永远不会改变”。
这是怎么回事
zText += "foo";
首先,Java 编译器将获取 zText String 实例的值,然后创建一个新的 String 实例,其值为 zText 附加“foo”。所以你知道为什么 zText 指向的实例没有改变。这完全是一个新的例子。事实上,甚至 String "foo" 也是一个新的 String 实例。所以,对于这条语句,Java会创建两个String实例,一个是“foo”,另一个是zText的值追加“foo”。 规则很简单:String 实例的值永远不会改变。
对于方法fillString,你可以使用StringBuffer作为参数,也可以这样改变:
String fillString(String zText)
return zText += "foo";
【讨论】:
...虽然如果您要按照此代码定义“fillString”,您真的应该给它一个更合适的名称! 是的。这个方法应该命名为“appendString”左右。【参考方案3】:您有三个选择:
使用 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 不是线程安全的。在多线程环境中使用 StringBuffer 类或手动处理同步。 @Boris Pavlovic - 是的,但除非不同的线程使用相同的 StringBuilder,在任何给定的情况下,IMO 都不太可能,这应该不是问题。方法是否被不同的线程调用,接收不同的StringBuilder也没关系。 我最喜欢第三个选项(数组),因为它是唯一通用的。它还允许传递布尔值、整数等。您能否详细解释一下此解决方案的性能 - 您声称从性能角度来看第一个更好。 @meolicStringBuilder
比 s += "..."
更快,因为它不会每次都分配新的 String 对象。事实上,当你编写像s = "Hello " + name
这样的Java 代码时,编译器会生成一个字节码,它会创建一个StringBuilder
并调用append()
两次。【参考方案4】:
Java 中的所有参数都是按值传递的。当您将 String
传递给函数时,传递的值是对 String 对象的引用,但您不能修改该引用,并且底层 String 对象是不可变的。
任务
zText += foo;
相当于:
zText = new String(zText + "foo");
也就是说,它(本地)将参数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);
打印:
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 的解决方案 - 请参阅 Effective Java 项“支持不变性”。
不过,如前所述,StringBuilder
通常会为您提供更好的性能 - 如果您有很多附加操作要做,尤其是在循环内,请使用 StringBuilder
。
但是如果可以的话,尝试传递不可变的Strings
而不是可变的StringBuilders
——你的代码会更容易阅读和维护。考虑将您的参数设置为final
,并配置您的 IDE,以便在您将方法参数重新分配给新值时发出警告。
【讨论】:
您说“严格来说”好像它几乎无关紧要 - 而它是问题的核心。如果Java 真的 有通过引用,那将不是问题。 请尽量避免传播“Java 通过引用传递对象”的神话 - 解释(正确的)“引用通过值传递”模型更有帮助,IMO。 嘿,我之前的评论去哪儿了? :-) 正如我之前所说,正如 Jon 现在所补充的,这里真正的问题是引用是按值传递的。这很重要,很多人不理解语义差异。 很公平。作为一名 Java 程序员,我在十多年的时间里没有看到任何实际上通过引用传递的东西,所以我的语言变得草率了。但你说得对,我应该更加小心,尤其是对于 C 编程的观众。【参考方案5】:为了在 java 中通过引用传递对象(包括字符串),您可以将其作为周围适配器的成员传递。一个泛型的解决方案在这里:
import java.io.Serializable;
public class ByRef<T extends Object> implements Serializable
private static final long serialVersionUID = 6310102145974374589L;
T v;
public ByRef(T v)
this.v = v;
public ByRef()
v = null;
public void set(T nv)
v = nv;
public T get()
return v;
// ------------------------------------------------------------------
static void fillString(ByRef<String> zText)
zText.set(zText.get() + "foo");
public static void main(String args[])
final ByRef<String> zText = new ByRef<String>(new String(""));
fillString(zText);
System.out.println(zText.get());
【讨论】:
【参考方案6】:java.lang.String 是不可变的。
我讨厌粘贴 URL,但 https://docs.oracle.com/javase/10/docs/api/java/lang/String.html 对于您在 Java 领域阅读和理解是必不可少的。
【讨论】:
我不认为 Soren Johnson 不知道 String 是什么,这不是问题,也不是我在使用 Java 15 年后来这里寻找的东西。【参考方案7】:字符串在java中是不可变的。您不能修改/更改现有的字符串文字/对象。
字符串 s="你好"; s=s+"你好";
这里之前的引用 s 被新的引用 s 替换,指向值 "HelloHi"。
然而,为了带来可变性,我们有 StringBuilder 和 StringBuffer。
StringBuilder s=new StringBuilder(); s.append("你好");
这会将新值“Hi”附加到相同的引用。 //
【讨论】:
【参考方案8】:对象通过引用传递,基元通过值传递。
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 总是按 VALUE 传递;诀窍是对象是由 reference 传递的,它是按值传递的。 (棘手,我知道)。检查这个:javadude.com/articles/passbyvalue.htm @adhg 你写的“对象是通过引用传递的,它是通过值传递的。” -1 你写了“对象是通过引用传递的” 在这里查看答案***.com/questions/40480/…【参考方案9】:答案很简单。在java中字符串是不可变的。因此,它就像使用“final”修饰符(或 C/C++ 中的“const”)。因此,一旦分配,您就无法像以前那样更改它。
您可以更改字符串指向的 which 值,但不能更改该字符串当前指向的实际值。
即。 String s1 = "hey"
。您可以设置s1 = "woah"
,这完全没问题,但是一旦使用 plusEquals 等(即@ 987654323@)。
为此,请使用 StringBuilder 或 StringBuffer 类或其他可变容器,然后只需调用 .toString() 将对象转换回字符串。
注意:字符串通常用作哈希键,因此这是它们不可变的部分原因。
【讨论】:
它是可变的还是不可变的并不相关。引用上的=
永远不会影响它曾经指向的对象,无论类如何。【参考方案10】:
在 Java 中 没有任何东西是通过引用传递的。一切都按价值传递。对象引用按值传递。此外,字符串是不可变的。因此,当您附加到传递的字符串时,您只会得到一个新的字符串。你可以使用返回值,或者传递一个 StringBuffer。
【讨论】:
嗯..不,您通过引用传递对象是一种误解。您正在按值传递引用。 是的,这是一种误解。这是一个巨大的、普遍的误解。它引出了一个我讨厌的面试问题:(“Java 如何传递参数”)。我讨厌它,因为大约一半的面试官实际上似乎想要错误的答案(“原始价值,对象引用”)。正确答案需要更长的时间才能给出,而且似乎使他们中的一些人感到困惑。他们不会被说服:我发誓我没有通过技术筛选,因为 CSMajor 类型的筛选器在大学里听到了这种误解,并相信它是福音。费。 @CPerkins 你不知道这让我有多生气。这让我热血沸腾。 所有语言中的一切都是按值传递的。其中一些值恰好是地址(引用)。 这完全是迂腐的观点,还是我遗漏了什么?我不明白这个答案的赞成票。它只是混淆了这个问题。【参考方案11】:到目前为止,Aaron Digulla 给出了最好的答案。他的第二个选项的一个变体是使用 commons lang 库版本 3+ 的包装器或容器类 MutableObject:
void fillString(MutableObject<String> c) c.setValue(c.getValue() + "foo");
你保存容器类的声明。缺点是依赖于 commons lang lib。但是这个 lib 有很多有用的功能,我从事的几乎所有大型项目都使用它。
【讨论】:
【参考方案12】: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");
另一种选择是使用 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";
【讨论】:
String 不是 Java 中的原始类型。 没错,但你到底想把我的注意力吸引到什么地方? 自从Java String 是原始的?!所以!通过引用传递的字符串。 '不可变对象'是我想说的。出于某种原因,我在@VWeber 发表评论后没有重新阅读我的答案,但现在我明白了他想说什么。我的错。答案已修改【参考方案13】:这个作品使用 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");
【讨论】:
【参考方案14】:发生的情况是引用是按值传递的,即传递了引用的副本。 java 中没有任何内容是通过引用传递的,并且由于字符串是不可变的,因此该赋值会创建一个新的字符串对象,引用的副本现在指向该对象。原来的引用仍然指向空字符串。
这对于任何对象都是一样的,即在方法中将其设置为新值。下面的例子只是让事情变得更明显,但连接一个字符串实际上是一回事。
void foo( object o )
o = new Object( ); // original reference still points to old value on the heap
【讨论】:
【参考方案15】:Java 中的字符串是immutable。
【讨论】:
以上是关于在Java中通过引用传递字符串?的主要内容,如果未能解决你的问题,请参考以下文章