当前位置: 首页 > 面试题库 >

Java字符串真的不可变吗?

公孙国兴
2023-03-14
问题内容

我们都知道这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  

为什么该程序会这样运行?为何值s1s2改变了,但没有改变s3


问题答案:

String 是不可变的*,但这仅意味着你无法使用其公共API对其进行更改。

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

现在,原因s1和s2变化值是它们都引用相同的实习字符串。编译器执行此操作(如其他答案所述)。

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

你可以安装SecurityManager,以避免恶意代码执行此类操作。但是请记住,某些库依赖于使用这些反射技巧(通常是ORM工具,AOP库等)。

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

由于这个话题似乎非常受欢迎,因此建议你进一步阅读以下内容:Heinz Kabutz在JavaZone 2009上发表的《 Reflection Madness》演讲,其中涵盖了OP中的许多问题以及其他反思……嗯……疯狂。

它涵盖了为什么有时有用。为什么,在大多数情况下,你应该避免使用它。



 类似资料:
  • 我们都知道在Java中是不可变的,但是检查下面的代码: 为什么这个程序会这样操作?为什么和的值被更改了,而却没有被更改?

  • 问题内容: 在 “ Swift编程语言” 的“字符串”部分的“字符串 可变性” 小节中,它表示: 您可以通过将某个变量分配给变量(在这种情况下可以修改)或常量(在这种情况下不能修改)来指示是否可以修改(或 变异 ): 并给出示例代码: 这本书中的iBooks 这里,或在Web浏览器在这里。 在下一段中,它声称“字符串是值类型”。 我的问题:对我来说,这看起来像个可变的字符串。看起来就像我在Java

  • 本文向大家介绍Java中的不可变字符串,包括了Java中的不可变字符串的使用技巧和注意事项,需要的朋友参考一下 在Java中,不可变对象是指那些不能更改或修改(一旦修改)数据的对象。字符串类是不可变的,即一旦我们创建了一个字符串对象,就无法修改其数据。

  • 据我所知, 可变字符串可以更改,不可变字符串不能更改。 这里我想这样改变字符串的值, 另一种方法是, 在这两种情况下,我都试图改变str的值。谁能告诉我,这两种情况的区别是什么,让我清楚地了解可变和不可变对象。

  • 为什么Java字符串被认为是不可变的?我可以说之后将name值更改为

  • 问题内容: 上面的代码片段中的代码产生以下输出。 a =’Hello’和b =’Hello’的长度分别为6和6,equals()为false 虽然两者的价值,并在控制台上显示的是,回报。怎么样? 问题答案: 和是 不是 可打印字符。它们都是控制字符,它们决定了文本应如何呈现-从左到右或从右到左。 您不会在终端中看到它们,并且它们不应该是等效的字符串。