Java - 字符串不变性和数组可变性
Java - String immutability and Array mutability
我知道数组 a
和 b
指向同一个位置,而字符串 s1
、s2
则不是。为什么?
代码:
String[] a = {"a","b","c"};
String[] b = a;
a[0] = "Z";
String s1 = "hello";
String s2 = s1;
s1 = "world";
System.out.println(Arrays.toString(a) + " - a"); //a and b are same
System.out.println(Arrays.toString(b) + " - b");
System.out.println(s1 + " "+ s2); // s1 and s2 are not same.
输出:
[Z, b, c] - a
[Z, b, c] - b
world hello
数组是对象,因此如果您更改数组元素,您会更改 a
和 b
指向的对象。
字符串是不可变对象,因此您只能更改引用,即调用 s1 = "world"
.
时您在做什么
更深入一点的解释:
字符串:
String s1 = "hello"; //creates a new String object with value "hello" and assigns a reference to that object to s1
String s2 = s1; //s2 now points to the same object as s1
s1 = "world"; //creates a new String object with value "world" and assigns a reference to that object to s1
所以最后s1会指向一个不同的字符串对象,值为"world"。
数组:
String[] a = {"a","b","c"}; //creates the array and assigns a reference to it to variable a
String[] b = a; //copies the reference of a to b, so both point to the same object
a[0] = "Z"; //changes the 1st element in the array, a and b still point to it
在这里您永远不会更改 a
的实际值,它是对数组的引用,但您更愿意更改数组的第一个元素的值,它本身是对字符串 "Z" 的引用。
在第 (2) 点,s1
和 s2
指向池中的相同文字。但是,当您更改 s1
时,会创建另一个文字,并且字符串不再指向同一个位置。
String s1 = "hello";
String s2 = s1; (2)
s1 = "world";
您可以通过打印
的结果来验证这一点
s1 == s2
请注意,我没有使用 equals
,因为我对比较参考感兴趣。现在,只要您为 s1
分配不同的值,事情就会变成这样:
+-------+
| world | <- s1
+-------+
+-------+
| hello | <- s2
+-------+
关键区别在于重新分配与修改。
重新赋值 (x = ...
) 使变量指向一个新对象但不改变底层对象,因此指向原始对象的任何其他变量将不会改变。
修改(x.something = ...
或 x[...] = ...
)仅更改基础对象,因此指向同一对象的任何其他变量都将反映更改。
请注意,字符串是不可变的 - 它们没有修改它们的方法,也没有(非最终)public 成员变量,因此它们不能被修改(没有反射)。
你可以认为人们指着书是你的变量,而书本身是底层对象。
多人可以指向同一本书。您可以让某人指向另一本书,而无需更改书籍本身或其他人指向的内容。如果你在一本书里写了一些东西,有人指着那本书,每个人都会看到这些变化。
int[] a = {0,1,2,3,4}; // Assign a to, let's say, "Object 1"
int[] b = {5,6,7,8}; // Assign b to, let's say, "Object 2"
int[] c = a; // Assign c to Object 1
a[0] = 2; // Change Object 1's 0th element
assert c[0] == 2; // c points to Object 1, whose 0th element is now 2
a = b; // Assign a to Object 2
assert c[0] == 2; // c still points to Object 1
a
和 b
是对同一个 Array 的引用(内存中只有一个 Array 对象。)
a ---> ["a", "b", "c"] <---- b
您正在使用此行更改此数组值:
a[0] = "Z"
所以你知道在内存中有这个:
a ---> ["Z", "b", "c"] <---- b
对于字符串,情况有所不同。
首先,您有两个指向相同值的变量:
String s1 = "hello";
String s2 = s1;
你的记忆中有这个:
s1 ---> "hello" <---- s2
但是,您使用以下代码将 s1 分配给一个新值:
s1 = "world";
变量s2仍然指向字符串"hello"。内存中现在有 2 个字符串对象。
s1 ---> "world"
s2 ---> "hello"
在Java中,字符串是不可变的,但数组是可变的。
另见 this question.
请注意,如果您定义自己的 class,行为将更接近数组。
public class Foo() {
private int _bar = 0;
public void setBar(int bar) {
this._bar = bar
}
public void getBar() {
return this._bar;
}
}
Foo f1 = new Foo();
Foo f1 = f2;
你有这个:
f1 ----> Foo [ _bar = 0 ] <---- f2
您可以处理对象:
f1.setBar(1)
f2.setBar(2) // This is the same object
这使数组有点 "like" :
f1 ----> Foo [ _bar = 2 ] <---- f2
但是如果你将 f2 赋给另一个值,你会得到这个:
f2 = new Foo();
在内存中创建一个新值,但仍保留第一个引用
指向第一个对象。
f1 ----> Foo [ _bar = 2 ]
f2 ----> Foo [ _bar = 0 ]
我知道数组 a
和 b
指向同一个位置,而字符串 s1
、s2
则不是。为什么?
代码:
String[] a = {"a","b","c"};
String[] b = a;
a[0] = "Z";
String s1 = "hello";
String s2 = s1;
s1 = "world";
System.out.println(Arrays.toString(a) + " - a"); //a and b are same
System.out.println(Arrays.toString(b) + " - b");
System.out.println(s1 + " "+ s2); // s1 and s2 are not same.
输出:
[Z, b, c] - a
[Z, b, c] - b
world hello
数组是对象,因此如果您更改数组元素,您会更改 a
和 b
指向的对象。
字符串是不可变对象,因此您只能更改引用,即调用 s1 = "world"
.
更深入一点的解释:
字符串:
String s1 = "hello"; //creates a new String object with value "hello" and assigns a reference to that object to s1
String s2 = s1; //s2 now points to the same object as s1
s1 = "world"; //creates a new String object with value "world" and assigns a reference to that object to s1
所以最后s1会指向一个不同的字符串对象,值为"world"。
数组:
String[] a = {"a","b","c"}; //creates the array and assigns a reference to it to variable a
String[] b = a; //copies the reference of a to b, so both point to the same object
a[0] = "Z"; //changes the 1st element in the array, a and b still point to it
在这里您永远不会更改 a
的实际值,它是对数组的引用,但您更愿意更改数组的第一个元素的值,它本身是对字符串 "Z" 的引用。
在第 (2) 点,s1
和 s2
指向池中的相同文字。但是,当您更改 s1
时,会创建另一个文字,并且字符串不再指向同一个位置。
String s1 = "hello";
String s2 = s1; (2)
s1 = "world";
您可以通过打印
的结果来验证这一点s1 == s2
请注意,我没有使用 equals
,因为我对比较参考感兴趣。现在,只要您为 s1
分配不同的值,事情就会变成这样:
+-------+
| world | <- s1
+-------+
+-------+
| hello | <- s2
+-------+
关键区别在于重新分配与修改。
重新赋值 (x = ...
) 使变量指向一个新对象但不改变底层对象,因此指向原始对象的任何其他变量将不会改变。
修改(x.something = ...
或 x[...] = ...
)仅更改基础对象,因此指向同一对象的任何其他变量都将反映更改。
请注意,字符串是不可变的 - 它们没有修改它们的方法,也没有(非最终)public 成员变量,因此它们不能被修改(没有反射)。
你可以认为人们指着书是你的变量,而书本身是底层对象。
多人可以指向同一本书。您可以让某人指向另一本书,而无需更改书籍本身或其他人指向的内容。如果你在一本书里写了一些东西,有人指着那本书,每个人都会看到这些变化。
int[] a = {0,1,2,3,4}; // Assign a to, let's say, "Object 1"
int[] b = {5,6,7,8}; // Assign b to, let's say, "Object 2"
int[] c = a; // Assign c to Object 1
a[0] = 2; // Change Object 1's 0th element
assert c[0] == 2; // c points to Object 1, whose 0th element is now 2
a = b; // Assign a to Object 2
assert c[0] == 2; // c still points to Object 1
a
和 b
是对同一个 Array 的引用(内存中只有一个 Array 对象。)
a ---> ["a", "b", "c"] <---- b
您正在使用此行更改此数组值:
a[0] = "Z"
所以你知道在内存中有这个:
a ---> ["Z", "b", "c"] <---- b
对于字符串,情况有所不同。
首先,您有两个指向相同值的变量:
String s1 = "hello";
String s2 = s1;
你的记忆中有这个:
s1 ---> "hello" <---- s2
但是,您使用以下代码将 s1 分配给一个新值:
s1 = "world";
变量s2仍然指向字符串"hello"。内存中现在有 2 个字符串对象。
s1 ---> "world"
s2 ---> "hello"
在Java中,字符串是不可变的,但数组是可变的。 另见 this question.
请注意,如果您定义自己的 class,行为将更接近数组。
public class Foo() {
private int _bar = 0;
public void setBar(int bar) {
this._bar = bar
}
public void getBar() {
return this._bar;
}
}
Foo f1 = new Foo();
Foo f1 = f2;
你有这个:
f1 ----> Foo [ _bar = 0 ] <---- f2
您可以处理对象:
f1.setBar(1)
f2.setBar(2) // This is the same object
这使数组有点 "like" :
f1 ----> Foo [ _bar = 2 ] <---- f2
但是如果你将 f2 赋给另一个值,你会得到这个:
f2 = new Foo();
在内存中创建一个新值,但仍保留第一个引用 指向第一个对象。
f1 ----> Foo [ _bar = 2 ]
f2 ----> Foo [ _bar = 0 ]