为什么下面的例子似乎反驳了字符串在Java中是不可变的对象?
我正在Ubuntu下使用OpenJDK Java编译器。我想把一个字符数组转换成一个字符串,当结果看起来不明确时,我试着写一个自己的为什么下面的例子似乎反驳了字符串在Java中是不可变的对象?,java,string,mutable,Java,String,Mutable,我正在Ubuntu下使用OpenJDK Java编译器。我想把一个字符数组转换成一个字符串,当结果看起来不明确时,我试着写一个自己的toString方法。在这个过程中,我编写了一个测试程序,其中(出于乐趣)我试图编译以下代码 class toString{ public static void main(String[] args){ string = "abc"; string = string + "bcd"; System.out.p
toString
方法。在这个过程中,我编写了一个测试程序,其中(出于乐趣)我试图编译以下代码
class toString{
public static void main(String[] args){
string = "abc";
string = string + "bcd";
System.out.println(string);
}
}
现在,我知道Java中的
String
对象是不可变的,代码实际上应该生成了一个错误,但令我惊讶的是,它将abcbcd
打印到了控制台。这是否意味着Java中的String
对象是可变的,或者在这种情况下OpenJDK编译器的实现有什么问题?没有,没有错误-您没有更改任何String对象的内容
您正在更改一个完全不同的字符串变量的值。将其视为两个操作:
- 创建新字符串时,表达式
string+“bcd”
- 将对新字符串的引用分配回
变量字符串
String string = "abc";
String other = string + "bcd";
// abc - neither the value of string nor the object's contents have changed
System.out.println(string);
// This is *just* changing the value of the string variable. It's not making
// any changes to the data within any objects.
string = other;
区分变量和对象是非常重要的。变量的值永远只是引用或基元类型值。更改变量的值不会更改其先前引用的对象的内容。
String
对象是不可变的,您只需在代码示例中将String
的值重新指定给String+“bcd”
。您不是在修改现有的字符串
对象,而是在创建一个新的对象并将其分配给旧名称。您上面发布的代码实际上并没有改变任何字符串,尽管看起来像是这样。原因是此行不会改变字符串:
string = string + "bcd";
相反,它的作用是:
string+“bcd”
string
引用的字符串以引用此新字符串string += "bcd";
它看起来更像是将bcd
连接到字符串的末尾,从而对其进行变异,即使它与上述代码等效,因此不会对实际的字符串
对象造成任何更改(同样,它通过创建一个新的字符串对象并更改引用的对象来工作。)
要了解此处发生的情况是您实际更改的是引用,而不是它引用的字符串,您可以尝试重写代码以使字符串最终,这将阻止您更改引用的对象。如果这样做,您会发现代码不再编译。例如:
class toString{
public static void main(String[] args){
final String string = "abc";
string = string + "bcd"; // Error: can't change string!
System.out.println(string);
}
}
最后一个注意事项—新Java程序员在使用String
s时感到悲伤的另一个常见原因是String
的方法似乎会改变字符串,但实际上不会。例如,此代码无法正常工作:
String s = "HELLO, WORLD!";
s.toLowerCase(); // Legal but incorrect
System.out.println(s); // Prints HELLO, WORLD!
在这里,调用s.toLowerCase()
实际上不会将字符串中的字符转换为小写,而是生成一个新字符串,其中的字符设置为小写
String s = "HELLO, WORLD!";
s = s.toLowerCase(); // Legal and correct
System.out.println(s); // Prints hello, world!
同样,这里的关键细节是对s
的赋值不会改变任何具体的String
对象,而只是调整对象s
所指的内容
希望这有帮助!string=string+“bcd”
将string
的新实例设置为变量string
,而不是修改该对象这样做的目的是创建一个新对象,并替换旧对象。
如果您想要可变字符串,请查看字符串生成器。字符串变量string
本身是可变的
字符串对象“abc”
是不可变的,字符串对象“bcd”
也是不可变的,连接的结果“abcbcd”
。最后一个结果被分配给变量
在执行被截断的代码时,没有字符串发生突变。字符串是不可变的……您的示例所做的只是显示您可以为变量分配新的字符串引用
如果我们对代码进行编译,并稍加修改:
class toString{
public static void main(String[] args){
String string = "abc";
System.out.println(string);
string = string + "bcd";
System.out.println(string);
}
}
您将看到“abc”和“abcbcd”,这可能会导致您认为字符串已更改,但它没有更改
当您执行字符串=/*which*/时,您正在用一个新值覆盖名为string的变量中的内容
如果字符串有一个方法,比如setCharAt(int index,char value),那么它是可变的。它不会反驳它。它实际上不会编译,因为字符串没有声明为字符串对象。但是,假设你的意思是:
class toString{
public static void main(String[] args){
String string = "abc";
string = string + "bcd";
System.out.println(string);
}
}
请参阅+运算符创建一个新字符串,并保留“abc”。原始的“abc”仍然存在,但您实际上所做的只是创建一个新字符串“abcbcd”,并在执行时覆盖对“abc”的原始引用:String=String+“bcd”。如果您将该代码更改为此,您将看到我的意思:
class toString {
public static void main(String[] args ) {
String originalString = "abc";
String newString = originalString + "bcd";
System.out.println( originalString ); // prints the original "abc";
System.out.println( newString ); // prints the new string "abcbcd";
}
}
区别在于对对象的引用和对象本身
String XXX = "xxx";
指:
创建一个新变量,并将引用分配给包含文本字符串“xxx”的对象字符串实例
指:
获取对变量XXX中对象的引用。
创建一个字符串类型的新对象,该对象包含字符串文字“yyy”。
执行字符串+运算符将它们相加
XXX = XXX + "yyy";
String a = "abc";
String b = "def";
String c = a;
a = a + b;
System.out.println(a); // will print "abcdef".
System.out.println(b); // will print "def".
System.out.println(c); // will print "abc".
// Now we compare references, in java == operator compare references, not the content of objects.
System.out.println(a == a); // Will print true
System.out.println(a == c); // Will print false, objects are not the same!
a = c;
System.out.println(a == c); // Will print true, now a and b points on the same instance.