Java中字符串的不变性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1552301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Immutability of Strings in Java
提问by Light_handle
Consider the following example.
考虑以下示例。
String str = new String();
str = "Hello";
System.out.println(str); //Prints Hello
str = "Help!";
System.out.println(str); //Prints Help!
Now, in Java, String objects are immutable. Then how come the object str
can be assigned value "Help!". Isn't this contradicting the immutability of strings in Java? Can anybody please explain me the exact concept of immutability?
现在,在 Java 中,String 对象是不可变的。那么为什么对象str
可以被赋值为“Help!”。这不是与 Java 中字符串的不变性相矛盾吗?任何人都可以向我解释不变性的确切概念吗?
Edit:
编辑:
Ok. I am now getting it, but just one follow-up question. What about the following code:
好的。我现在明白了,但只是一个后续问题。下面的代码怎么样:
String str = "Mississippi";
System.out.println(str); // prints Mississippi
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
Does this mean that two objects are created again ("Mississippi" and "M!ss!ss!pp!") and the reference str
points to a different object after replace()
method?
这是否意味着再次创建了两个对象(“Mississippi”和“M!ss!ss!pp!”)并且str
在replace()
方法之后引用指向不同的对象?
采纳答案by gustafc
str
is not an object, it's a reference to an object. "Hello"
and "Help!"
are two distinct String
objects. Thus, str
points toa string. You can change what it points to, but not that which it points at.
str
不是一个对象,它是一个对象的引用。"Hello"
并且"Help!"
是两个不同的String
对象。因此,str
指向一个字符串。您可以更改它指向的内容,但不能更改它指向的内容。
Take this code, for example:
以这段代码为例:
String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"
Now, there is nothing1we could do to s1
that would affect the value of s2
. They refer to the same object - the string "Hello"
- but that object is immutable and thus cannot be altered.
现在,没有什么1,我们可以做些什么来s1
会影响价值s2
。它们引用同一个对象——字符串"Hello"
——但该对象是不可变的,因此不能改变。
If we do something like this:
如果我们做这样的事情:
s1 = "Help!";
System.out.println(s2); // still prints "Hello"
Here we see the difference between mutating an object, and changing a reference. s2
still points to the same object as we initially set s1
to point to. Setting s1
to "Help!"
only changes the reference, while the String
object it originally referred to remains unchanged.
在这里,我们看到了改变对象和改变引用之间的区别。s2
仍然指向我们最初设置s1
指向的同一个对象。设置s1
为"Help!"
仅更改引用,而String
它最初引用的对象保持不变。
If strings weremutable, we could do something like this:
如果字符串是可变的,我们可以这样做:
String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"
Edit to respond to OP's edit:
编辑以响应 OP 的编辑:
If you look at the source code for String.replace(char,char)(also available in src.zip in your JDK installation directory -- a pro tip is to look there whenever you wonder how something really works) you can see that what it does is the following:
如果您查看String.replace(char,char)的源代码(也可以在您的 JDK 安装目录中的 src.zip 中找到——专业提示是每当您想知道某些东西是如何真正工作时就查看那里),您可以看到它的作用如下:
- If there is one or more occurrences of
oldChar
in the current string, make a copy of the current string where all occurrences ofoldChar
are replaced withnewChar
. - If the
oldChar
is not present in the current string, return the current string.
- 如果
oldChar
当前字符串中出现一个或多个,则复制当前字符串,其中所有出现的oldChar
都被替换为newChar
。 - 如果
oldChar
当前字符串中不存在 ,则返回当前字符串。
So yes, "Mississippi".replace('i', '!')
creates a new String
object. Again, the following holds:
所以是的,"Mississippi".replace('i', '!')
创建一个新String
对象。同样,以下内容成立:
String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects
Your homework for now is to see what the above code does if you change s1 = s1.replace('i', '!');
to s1 = s1.replace('Q', '!');
:)
你现在做的功课,看看上面的代码确实如果你改变s1 = s1.replace('i', '!');
来s1 = s1.replace('Q', '!');
:)
1Actually, it ispossible to mutate strings (and other immutable objects). It requires reflection and is very, very dangerous and should never ever be used unless you're actually interested in destroying the program.
1实际上,它是可能的突变的字符串(和其他不可变的对象)。它需要反思并且非常非常危险,除非您真的有兴趣破坏程序,否则永远不要使用它。
回答by coobird
The object that str
references can change, but the actual String
objects themselves cannot.
str
引用的对象可以改变,但实际的String
对象本身不能。
The String
objects containing the string "Hello"
and "Help!"
cannot change their values, hence they are immutable.
所述String
对象包含字符串"Hello"
和"Help!"
不能改变它们的值,因此,它们是不可变的。
The immutability of String
objects does not mean that the references pointing to the object cannot change.
String
对象的不变性并不意味着指向该对象的引用不能改变。
One way that one can prevent the str
reference from changing is to declare it as final
:
可以防止str
引用更改的一种方法是将其声明为final
:
final String STR = "Hello";
Now, trying to assign another String
to STR
will cause a compile error.
现在,尝试分配另一个String
toSTR
将导致编译错误。
回答by daveb
The string object that was first referenced by str
was not altered, all that you did was make str
refer to a new string object.
首先引用的字符串对象str
没有改变,您所做的只是str
引用一个新的字符串对象。
回答by akf
The String will not change, the reference to it will. You are confusing immutability with the concept of final
fields. If a field is declared as final
, once it has been assigned, it cannot be reassigned.
String 不会改变,对它的引用会改变。您将不变性与final
字段的概念混淆了。如果一个字段被声明为final
,一旦它被分配,就不能重新分配。
回答by Preston Guillot
Immutability implies that the value of an instantiated object cannot change, you can never turn "Hello" into "Help!".
不变性意味着实例化对象的值不能改变,你永远不能把“Hello”变成“Help!”。
The variable str is a reference to an object, when you assign a new value to str you aren't changing the value of the object it references, you are referencing a different object.
变量 str 是对对象的引用,当您为 str 分配新值时,您并没有更改它引用的对象的值,而是引用了不同的对象。
回答by twolfe18
Though java tries to ignore it, str
is nothing more than a pointer. This means that when you first write str = "Hello";
, you create an object that str
points to. When you reassign str
by writing str = "Help!";
, a new object is created and the old "Hello"
object gets garbage collected whenever java feels like it.
尽管 java 试图忽略它,但它str
只不过是一个指针。这意味着当您第一次写入时str = "Hello";
,您创建了一个str
指向的对象。当您str
通过write 重新分配时str = "Help!";
,会创建一个新对象,并且"Hello"
只要 java 感觉它就会对旧对象进行垃圾收集。
回答by Michael Lloyd Lee mlk
Light_handle I recommend you take a read of Cup Size -- a story about variablesand Pass-by-Value Please (Cup Size continued). This will help a lot when reading the posts above.
Light_handle 我建议您阅读Cup Size - 一个关于变量和值传递的故事(Cup Size 续)。这在阅读上面的帖子时会有很大帮助。
Have you read them? Yes. Good.
你读过它们吗?是的。好的。
String str = new String();
This creates a new "remote control" called "str
" and sets that to the value new String()
(or ""
).
这将创建一个名为“ str
”的新“远程控制”并将其设置为值new String()
(或""
)。
e.g. in memory this creates:
例如在内存中这会创建:
str --- > ""
str = "Hello";
This then changes the remote control "str
" but does not modify the original string ""
.
这随后会更改遥控器“ str
”,但不会修改原始字符串""
。
e.g. in memory this creates:
例如在内存中这会创建:
str -+ ""
+-> "Hello"
str = "Help!";
This then changes the remote control "str
" but does not modify the original string ""
or the object that the remote control currently points to.
这随后会更改遥控器“ str
”,但不会修改""
遥控器当前指向的原始字符串或对象。
e.g. in memory this creates:
例如在内存中这会创建:
str -+ ""
| "Hello"
+-> "Help!"
回答by user85421
Regarding the replace part of your question, try this:
关于您问题的替换部分,请尝试以下操作:
String str = "Mississippi";
System.out.println(str); //Prints Mississippi
String other = str.replace("i", "!");
System.out.println(str); //still prints Mississippi
System.out.println(other); // prints M!ss!ss!pp!
回答by martynas
For those wondering how to break String immutability in Java...
对于那些想知道如何打破 Java 中字符串不变性的人...
Code
代码
import java.lang.reflect.Field;
public class StringImmutability {
public static void main(String[] args) {
String str1 = "I am immutable";
String str2 = str1;
try {
Class str1Class = str1.getClass();
Field str1Field = str1Class.getDeclaredField("value");
str1Field.setAccessible(true);
char[] valueChars = (char[]) str1Field.get(str1);
valueChars[5] = ' ';
valueChars[6] = ' ';
System.out.println(str1 == str2);
System.out.println(str1);
System.out.println(str2);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Output
输出
true
I am mutable
I am mutable
回答by Nayan Srivastava
In Java, objects are generally accessed by references. In your piece of code str is a reference which is first assigned to "Hello" (an automatic created object or fetched from constant pool) and then you assigned another object "Help!" to same reference. A point to note is the reference is the same and modified, but objects are different. One more thing in your code you accessed three objects,
在 Java 中,对象通常通过引用访问。在您的代码段中 str 是一个引用,它首先分配给“Hello”(自动创建的对象或从常量池中获取),然后您分配另一个对象“帮助!” 到相同的参考。需要注意的一点是引用是相同的和修改过的,但对象不同。在您访问三个对象的代码中还有一件事,
- When you called new String().
- When you assigned "hello".
- When you assigned "help!".
- 当您调用 new String() 时。
- 当你分配“你好”时。
- 当您分配“帮助!”时。
Calling new String() creates a new object even if it exists in string pool, so generally it should not be used. To put a string created from new String () into string pool you can try the intern()
method.
调用 new String() 会创建一个新对象,即使它存在于字符串池中,所以一般不应该使用它。要将 new String() 创建的字符串放入字符串池中,您可以尝试该intern()
方法。
I hope this helps.
我希望这有帮助。