Java 字符串真的不可变吗? [英] Is a Java string really immutable?

查看:27
本文介绍了Java 字符串真的不可变吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们都知道String在Java中是不可变的,但请检查以下代码:

We all know that String is immutable in Java, but check the following code:

String s1 = "Hello World";  
String s2 = "Hello World";  
String s3 = s1.substring(6);  
System.out.println(s1); // Hello World  
System.out.println(s2); // Hello World  
System.out.println(s3); // World  

Field field = String.class.getDeclaredField("value");  
field.setAccessible(true);  
char[] value = (char[])field.get(s1);  
value[6] = 'J';  
value[7] = 'a';  
value[8] = 'v';  
value[9] = 'a';  
value[10] = '!';  

System.out.println(s1); // Hello Java!  
System.out.println(s2); // Hello Java!  
System.out.println(s3); // World  

为什么这个程序会这样运行?为什么 s1s2 的值改变了,而 s3 没有改变?

Why does this program operate like this? And why is the value of s1 and s2 changed, but not s3?

推荐答案

String 是不可变的* 但这仅意味着您不能使用其公共 API 更改它.

String is immutable* but this only means you cannot change it using its public API.

您在这里所做的是使用反射来绕过正常的 API.同样,您可以更改枚举的值,更改整数自动装箱中使用的查找表等.

What you are doing here is circumventing the normal API, using reflection. The same way, you can change the values of enums, change the lookup table used in Integer autoboxing etc.

现在,s1s2 更改值的原因是它们都引用了同一个内部字符串.编译器执行此操作(如其他答案所述).

Now, the reason s1 and s2 change value, is that they both refer to the same interned string. The compiler does this (as mentioned by other answers).

s3的原因实际上让我有点惊讶,因为我认为它会共享 value 数组(它在 Java 7u6 之前的早期版本中做到了).但是,查看String的源代码,我们可以看到实际上是复制了子字符串的value字符数组(使用Arrays.copyOfRange(..)).这就是它保持不变的原因.

The reason s3 does not was actually a bit surprising to me, as I thought it would share the value array (it did in earlier version of Java, before Java 7u6). However, looking at the source code of String, we can see that the value character array for a substring is actually copied (using Arrays.copyOfRange(..)). This is why it goes unchanged.

你可以安装一个SecurityManager,避免恶意代码做这样的事情.但请记住,某些库依赖于使用这些反射技巧(通常是 ORM 工具、AOP 库等).

You can install a SecurityManager, to avoid malicious code to do such things. But keep in mind that some libraries depend on using these kind of reflection tricks (typically ORM tools, AOP libraries etc).

*) 我最初写道 String 并不是真正不可变的,只是有效的不可变".这在 String 的当前实现中可能会产生误导,其中 value 数组确实被标记为 private final.不过,仍然值得注意的是,在 Java 中无法将数组声明为不可变的,因此必须注意不要将其暴露在其类之外,即使使用适当的访问修饰符也是如此.

*) I initially wrote that Strings aren't really immutable, just "effective immutable". This might be misleading in the current implementation of String, where the value array is indeed marked private final. It's still worth noting, though, that there is no way to declare an array in Java as immutable, so care must be taken not to expose it outside its class, even with the proper access modifiers.

由于这个话题似乎非常受欢迎,这里有一些建议进一步阅读:Heinz Kabutz 的 Reflection Madness talk 来自JavaZone 2009,它涵盖了 OP 中的许多问题,以及其他反思......好吧......疯狂.

As this topic seems overwhelmingly popular, here's some suggested further reading: Heinz Kabutz's Reflection Madness talk from JavaZone 2009, which covers a lot of the issues in the OP, along with other reflection... well... madness.

它介绍了为什么这有时很有用.为什么,大多数时候,你应该避免它.:-)

It covers why this is sometimes useful. And why, most of the time, you should avoid it. :-)

这篇关于Java 字符串真的不可变吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆