我习惯在C
做以下事情:
void main() {
String zText = "";
fillString(zText);
printf(zText);
}
void fillString(String zText) {
zText += "foo";
}
输出是:
foo
但是,在Java中,这似乎不起作用。我假设因为String
对象被复制而不是被引用传递。我认为字符串是对象,它总是通过引用传递。
这里发生了什么?
你有三个选择:
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中的immutable。
这可以使用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中是不可变的。您无法修改/更改现有的字符串文字/对象。
String s =“Hello”; S = S + “喜”;
这里先前的引用被替换为指向值“Hello Hi”的新引用。
但是,为了实现可变性,我们有StringBuilder和StringBuffer。
StringBuilder s = new StringBuilder(); s.append( “你好”);
这会将新值“Hi”附加到相同的引用s。 //
到目前为止,Aaron Digulla有最好的答案。他的第二个选项的变体是使用commons lang库版本3+的包装器或容器类MutableObject:
void fillString(MutableObject<String> c) { c.setValue(c.getValue() + "foo"); }
您保存容器类的声明。缺点是依赖于commons lang lib。但是lib有很多有用的功能,几乎任何我工作过的大项目都使用它。
对于在java中通过引用传递对象(包括String),您可以将其作为周围适配器的成员传递。这里有一个通用的解决方案:
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());
}
}
对于更好奇的人
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
如何随String变量更改。当s被改变时,在函数changeto
中将新对象分配给变量s
在Java中没有任何内容通过引用传递。一切都是通过价值传递的。对象引用按值传递。另外字符串是不可变的。因此,当您附加到传递的String时,您只需获得一个新的String。您可以使用返回值,或者传递StringBuffer。
发生的事情是引用按值传递,即传递引用的副本。 java中没有任何内容通过引用传递,并且由于字符串是不可变的,因此该赋值创建了一个新的字符串对象,该引用的副本现在指向该对象。原始引用仍指向空字符串。
这对任何对象都是相同的,即在方法中将其设置为新值。下面的例子只是让事情变得更加明显,但连接一个字符串实际上是一回事。
void foo( object o )
{
o = new Object( ); // original reference still points to old value on the heap
}
java.lang.String是不可变的。
我讨厌粘贴URL,但如果你在java-land中,https://docs.oracle.com/javase/10/docs/api/java/lang/String.html对于你阅读和理解是必不可少的。
对象通过引用传递,基元通过值传递。
字符串不是基元,它是一个对象,它是对象的特例。
这是为了节省内存。在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));
}
}
/ * OUTPUT * /
a==b:true
a==c:true
a==d:false
a.equals(d):true
o1==o2:false
passString:true
passString:false
==正在检查内存地址(引用),.equals正在检查内容(值)
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项目“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");
}
另一个选择是创建一个类,其中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";
}
答案很简单。在java中,字符串是不可变的。因此它就像使用'final'修饰符(或C / C ++中的'const')。因此,一旦分配,您就无法像您所做的那样改变它。
您可以更改字符串指向的值,但不能更改此字符串当前指向的实际值。
IE浏览器。 String s1 = "hey"
。你可以制作s1 = "woah"
,这完全没问题,但你实际上不能将字符串的基础值(在这种情况下:“嘿”)更改为使用plusEquals等分配的其他内容(即.s1 += " whatup != "hey whatup"
)。
为此,请使用StringBuilder或StringBuffer类或其他可变容器,然后调用.toString()将对象转换回字符串。
注意:字符串通常用作哈希键,因此这是它们不可变的部分原因。
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";
}