考虑下面的例子。
String str = new String();
str = "Hello";
System.out.println(str); //Prints Hello
str = "Help!";
System.out.println(str); //Prints Help!
在Java中,String对象是不可变的。那么为什么对象str可以被赋值为"Help!"呢?这难道不是与Java中字符串的不变性相矛盾吗?有人能给我解释一下不变性的确切概念吗?
编辑:
好的。我现在明白了,但还有一个问题。下面的代码呢:
String str = "Mississippi";
System.out.println(str); // prints Mississippi
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
这是否意味着将再次创建两个对象(“Mississippi”和“M!ss!ss!pp!”),并且在replace()方法之后引用str指向不同的对象?
让我们把它分成几个部分
String s1 = "hello";
此语句创建包含hello的字符串并占用内存空间,即在常量字符串池中,并将其分配给引用对象s1
String s2 = s1;
这条语句将相同的字符串hello赋值给新引用s2
__________
| |
s1 ---->| hello |<----- s2
|__________|
两个引用都指向相同的字符串,因此输出相同的值,如下所示。
out.println(s1); // o/p: hello
out.println(s2); // o/p: hello
虽然String是不可变的,但可以赋值,因此s1现在将引用新的值堆栈。
s1 = "stack";
__________
| |
s1 ---->| stack |
|__________|
但是指向hello的s2对象呢它还是原来的样子。
__________
| |
s2 ---->| hello |
|__________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
由于字符串是不可变的,Java虚拟机不允许我们通过它的方法修改字符串s1。它将在池中创建所有新的String对象,如下所示。
s1.concat(" overflow");
___________________
| |
s1.concat ----> | stack overflow |
|___________________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
out.println(s1.concat); // o/p: stack overflow
注意,如果String将是可变的,那么输出将是
out.println(s1); // o/p: stack overflow
现在,您可能会感到惊讶,为什么String需要修改concat()这样的方法。下面的代码片段将消除您的困惑。
s1 = s1.concat(" overflow");
这里我们将修改后的string值赋值给s1引用。
___________________
| |
s1 ---->| stack overflow |
|___________________|
out.println(s1); // o/p: stack overflow
out.println(s2); // o/p: hello
这就是为什么Java决定将String作为最终类,否则任何人都可以修改和改变String的值。
希望这能有所帮助。
str引用的对象可以更改,但实际的String对象本身不能更改。
包含字符串“Hello”和“Help!”的String对象不能改变它们的值,因此它们是不可变的。
String对象的不可变性并不意味着指向该对象的引用不能改变。
防止str引用更改的一种方法是将其声明为final:
final String STR = "Hello";
现在,尝试将另一个String赋值给STR将导致编译错误。
让我们把它分成几个部分
String s1 = "hello";
此语句创建包含hello的字符串并占用内存空间,即在常量字符串池中,并将其分配给引用对象s1
String s2 = s1;
这条语句将相同的字符串hello赋值给新引用s2
__________
| |
s1 ---->| hello |<----- s2
|__________|
两个引用都指向相同的字符串,因此输出相同的值,如下所示。
out.println(s1); // o/p: hello
out.println(s2); // o/p: hello
虽然String是不可变的,但可以赋值,因此s1现在将引用新的值堆栈。
s1 = "stack";
__________
| |
s1 ---->| stack |
|__________|
但是指向hello的s2对象呢它还是原来的样子。
__________
| |
s2 ---->| hello |
|__________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
由于字符串是不可变的,Java虚拟机不允许我们通过它的方法修改字符串s1。它将在池中创建所有新的String对象,如下所示。
s1.concat(" overflow");
___________________
| |
s1.concat ----> | stack overflow |
|___________________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
out.println(s1.concat); // o/p: stack overflow
注意,如果String将是可变的,那么输出将是
out.println(s1); // o/p: stack overflow
现在,您可能会感到惊讶,为什么String需要修改concat()这样的方法。下面的代码片段将消除您的困惑。
s1 = s1.concat(" overflow");
这里我们将修改后的string值赋值给s1引用。
___________________
| |
s1 ---->| stack overflow |
|___________________|
out.println(s1); // o/p: stack overflow
out.println(s2); // o/p: hello
这就是为什么Java决定将String作为最终类,否则任何人都可以修改和改变String的值。
希望这能有所帮助。
public final class String_Test {
String name;
List<String> list=new ArrayList<String>();
public static void main(String[] args) {
String_Test obj=new String_Test();
obj.list.add("item");//List will point to a memory unit- i.e will have one Hashcode value #1234
List<String> list2=obj.list; //lis1 also will point to same #1234
obj.list.add("new item");//Hashcode of list is not altered- List is mutable, so reference remains same, only value in that memory location changes
String name2=obj.name="Myname"; // name2 and name will point to same instance of string -Hashcode #5678
obj.name = "second name";// String is Immutable- New String HAI is created and name will point to this new instance- bcoz of this Hashcode changes here #0089
System.out.println(obj.list.hashCode());
System.out.println(list2.hashCode());
System.out.println(list3.hashCode());
System.out.println("===========");
System.out.println(obj.name.hashCode());
System.out.println(name2.hashCode());
}
}
会产生这样的东西吗
1419358369
1419358369
103056
65078777
不可变对象的目的是它的值一旦被赋值就不应该被改变。
它将返回新对象,每次你试图改变它基于实现。
注意:可以使用Stringbuffer而不是string来避免这种情况。
对于你的最后一个问题::u将有一个引用,在字符串池中有2个字符串。
除了参考将指向m!ss!ss!pp!