本文翻译自:Is a Java string really immutable?

We all know that String is immutable in Java, but check the following code: 我们都知道String在Java中是不可变的,但是请检查以下代码:

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

Why does this program operate like this? 为什么该程序会这样运行? And why is the value of s1 and s2 changed, but not s3 ? 为什么s1s2的值改变了,而s3却没有改变?


#1楼

参考:https://stackoom.com/question/1PslN/Java字符串真的不可变吗


#2楼

You are using reflection to access the "implementation details" of string object. 您正在使用反射来访问字符串对象的“实现细节”。 Immutability is the feature of the public interface of an object. 不变性是对象公共接口的功能。


#3楼

You are using reflection to circumvent the immutability of String - it's a form of "attack". 您正在使用反射来规避String的不变性-这是“攻击”的一种形式。

There are lots of examples you can create like this (eg you can even instantiate a Void object too), but it doesn't mean that String is not "immutable". 您可以创建许多这样的示例(例如,甚至可以实例化Void对象 ),但这并不意味着String不是“不可变的”。

There are use cases where this type of code may be used to your advantage and be "good coding", such as clearing passwords from memory at the earliest possible moment (before GC) . 在某些情况下,这种类型的代码可以为您带来好处,并且是“良好的编码”,例如, 尽早(在GC之前)从内存中清除密码 。

Depending on the security manager, you may not be able to execute your code. 根据安全管理器的不同,您可能无法执行代码。


#4楼

String is immutable* but this only means you cannot change it using its public API. String是不可变的*,但这仅意味着您无法使用其公共API对其进行更改。

What you are doing here is circumventing the normal API, using reflection. 您在这里所做的是使用反射来绕过常规API。 The same way, you can change the values of enums, change the lookup table used in Integer autoboxing etc. 同样,您可以更改枚举的值,更改整数自动装箱中使用的查找表等。

Now, the reason s1 and s2 change value, is that they both refer to the same interned string. 现在, s1s2更改值的原因是它们都引用相同的内联字符串。 The compiler does this (as mentioned by other answers). 编译器执行此操作(如其他答案所述)。

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). 究其原因s3 并不竟是一点令我感到诧异,因为我认为这将分享value阵列( 它没有在Java中的早期版本 ,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(..) ). 但是,查看String的源代码,我们可以看到实际上已复制了子字符串的value字符数组(使用Arrays.copyOfRange(..) )。 This is why it goes unchanged. 这就是为什么它保持不变。

You can install a SecurityManager , to avoid malicious code to do such things. 您可以安装SecurityManager ,以避免恶意代码执行此类操作。 But keep in mind that some libraries depend on using these kind of reflection tricks (typically ORM tools, AOP libraries etc). 但是请记住,某些库依赖于使用这种反射技巧(通常是ORM工具,AOP库等)。

*) I initially wrote that String s aren't really immutable, just "effective immutable". *)我最初写道String并不是真正的不变,而只是“有效的不变”。 This might be misleading in the current implementation of String , where the value array is indeed marked private final . String的当前实现中,这可能会产生误导,其中value数组的确标记为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. 但是,仍然值得注意的是,没有办法在Java中将数组声明为不可变的,因此即使使用适当的访问修饰符,也必须注意不要将其暴露在类之外。


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. 由于该主题似乎非常受欢迎,因此,建议您进一步阅读以下内容: Heinz Kabutz在JavaZone 2009上发表的《 Reflection Madness》演讲 ,其中涵盖了OP中的许多问题以及其他反思……嗯……疯狂。

It covers why this is sometimes useful. 它涵盖了为什么有时有用。 And why, most of the time, you should avoid it. 为什么,在大多数情况下,您应该避免使用它。 :-) :-)


#5楼

String immutability is from the interface perspective. 字符串不变性是从接口角度来看的。 You are using reflection to bypass the interface and directly modify the internals of the String instances. 您正在使用反射绕过接口并直接修改String实例的内部。

s1 and s2 are both changed because they are both assigned to the same "intern" String instance. s1s2都被更改,因为它们都分配给了相同的“ intern”字符串实例。 You can find out a bit more about that part from this article about string equality and interning. 您可以从本文中有关字符串相等和实习的那部分中找到更多信息。 You might be surprised to find out that in your sample code, s1 == s2 returns true ! 您可能会惊讶地发现在示例代码中, s1 == s2返回true


#6楼

Visibility modifiers and final (ie immutability) are not a measurement against malicious code in Java; 可见性修饰符和最终修饰符(即不变性)并不是针对Java中恶意代码的度量; they are merely tools to protect against mistakes and to make the code more maintainable (one of the big selling points of the system). 它们仅是防止错误并使代码更易于维护的工具(系统的最大卖点之一)。 That is why you can access internal implementation details like the backing char array for String s via reflection. 这就是为什么您可以通过反射访问内部实现详细信息(例如String的后备char数组)的原因。

The second effect you see is that all String s change while it looks like you only change s1 . 您看到的第二个效果是,所有String改变了,而看起来您只改变了s1 It is a certain property of Java String literals that they are automatically interned, ie cached. Java String文字的某些属性是它们会自动进行intern(即缓存)。 Two String literals with the same value will actually be the same object. 具有相同值的两个String文字实际上将是同一对象。 When you create a String with new it will not be interned automatically and you will not see this effect. 当您使用new创建一个String时,它不会被自动检查,也不会看到这种效果。

#substring until recently (Java 7u6) worked in a similar way, which would have explained the behaviour in the original version of your question. #substring直到最近(Java的7u6)以类似的方式,这将在你的问题的原始版本已经解释行为的工作。 It didn't create a new backing char array but reused the one from the original String; 它没有创建一个新的支持char数组,而是重用了原始String中的那个数组。 it just created a new String object that used an offset and a length to present only a part of that array. 它只是创建了一个新的String对象,该对象使用一个偏移量和一个长度来仅表示该数组的一部分。 This generally worked as Strings are immutable - unless you circumvent that. 通常,这是因为Strings是不可变的-除非您对此进行规避。 This property of #substring also meant that the whole original String couldn't be garbage collected when a shorter substring created from it still existed. #substring此属性还意味着当仍然存在从其原始字符串创建的较短子字符串时,无法对整个原始String进行垃圾回收。

As of current Java and your current version of the question there is no strange behaviour of #substring . 从当前的Java和问题的当前版本开始, #substring没有奇怪的行为。

Java字符串真的不可变吗?相关推荐

  1. java 字符串赋值_灵魂拷问:为什么 Java 字符串是不可变的?

    在逛 programcreek 的时候,发现了一些精妙绝伦的主题.比如说:为什么 Java 字符串是不可变的?像这类灵魂拷问的主题,非常值得深思.对于绝大多数的初级程序员来说,往往停留在"知 ...

  2. 灵魂拷问:为什么 Java 字符串是不可变的?

    在逛 programcreek 的时候,发现了一些精妙绝伦的主题.比如说:为什么 Java 字符串是不可变的?像这类灵魂拷问的主题,非常值得深思. 对于绝大多数的初级程序员来说,往往停留在" ...

  3. 聊一聊Java字符串的不可变

    点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 前言 在 Java 开发中 String (字符串)对象是我们使用最频繁的对象,也是很重要的 ...

  4. 再见,Java字符串是不可变的

    最近,又有好几个小伙伴问我这个问题:"二哥,为什么 Java 的 String 要设计成不可变的啊?"说实话,这也是一道非常经典的面试题,面试官超喜欢问.我之前写过这方面的文章,现 ...

  5. java字符串不可变_Java字符串真的是不可变的吗?

    Java字符串真的是不可变的吗? 我们都知道StringJava 中是不可变的,但请检查以下代码: String s1 = "Hello World"; String s2 = & ...

  6. java 字符串面试_Java字符串面试问答

    java 字符串面试 String is one of the most widely used Java Class. Here I am listing some important Java S ...

  7. java string char数组_String在Java中真的是不可变吗?

    我们都知道 Java 中的 String 类的设计是不可变的,来看下 String 类的源码. public final class Stringimplements java.io.Serializ ...

  8. 你真的理解Java 字符串的不可变性吗?

    作者:明明如月学长, CSDN 博客专家,蚂蚁集团高级 Java 工程师,<性能优化方法论>作者.<解锁大厂思维:剖析<阿里巴巴Java开发手册>>.<再学经 ...

  9. Java 1.1.3 修改字符串、不可变字符串

    修改字符串 String类没有提供用于修改字符串的方法.如果希望将 greeting 的内容修改为" Help!", 不能直接地将 greeting的最后两个位置的字符修改为 ' ...

最新文章

  1. SSIS同步多个数据库
  2. hellocharts-android开源图表库(效果非常好)
  3. mybatis多对一处理两种处理方式
  4. while中的break
  5. python面试必备-基础篇
  6. 有图形化显示,继承WebControl类
  7. python面对对象的编程语言_python面对对象编程
  8. 数据抽取工具——DMCTextFilter V4.2(纯文本抽出通用程序库)
  9. 教你6步从头写机器学习算法——以感知机算法为例
  10. 数字通信系统的性能及可靠性
  11. 【Win10 C盘压缩卷问题解答】:无法将卷压缩到超出任何不可移动的文件所在点
  12. 亚马逊跨境电商如何运营模式?
  13. Hadoop Ha (High avilable)配置
  14. 计算机编程英语单词多少,计算机编程常用英语单词
  15. 标签分发协议(LDP)
  16. java-php-python-ssm“花花世界”网站计算机毕业设计
  17. BGP协议详解及工作原理
  18. 1.Spring学习笔记_HelloWorld(by尚硅谷_佟刚)
  19. 第5章第27节:如何录制幻灯片的演示过程 [PowerPoint精美幻灯片实战教程]
  20. Android11(30)/Android10(29)分区存储-存储访问框架(SAF)

热门文章

  1. Android开发之使用BroadcastReceiver实时监听电量(源代码分享)
  2. 理解 Android 的 Binder 机制
  3. 监听屏幕 android.intent.action.USER_PRESENT
  4. android 文件下载 超简单
  5. Android基础--tools:context=.TestActivity作用
  6. integer是值传递还是引用传递_数据值Value传递-高位传递
  7. Android性能优化典范第三季
  8. php链接数据库实行增删查改_利用PHP连接数据库——实现用户数据的增删改查的整体操作实例...
  9. python框架实例_Python之Flask框架项目Demo入门
  10. [Bzoj1061][Noi2008]志愿者招募(费用流)