Java中的String对象是不可变的吗
2014-02-19 22:43
155 查看
有个仁兄在 StackOverflow 上发起了一个问题,是这么问的:
众所周知Java中的String对象是不可变的,但我们来看下面这段代码:
为什么这段代码会是这样的运行结果?为什么s1和s2的值被改变了,但是s3的值却没有?
回答#1:
String对象是不可变的,但这仅意味着你无法通过调用它的公有方法来改变它的值。
上面的代码通过反射机制绕过了正常的API。采用这种方式,你还可以改变枚举的值,甚至可以改变Integer类型自动装箱时使用的查找表。
在这里,由于s1和s2指向同一个内部的字符串对象,因此他们的值都被改变了。正如其他回答所述,这是由编译器实现的。
s3没有被改变的原因确实令我诧异,我过去一直认为s3和s1共享同一个value数组(在Java
7u6之前的版本中的确是这样)。然而,通过查看String类的源码,我们可以看出子串对象的value数组是从原字符串对象中拷贝得到的(通过使用Arrays.copyOfRange(..)方法)。这是s3没有被改变的原因。
你可以安装一个SecurityManager来防止恶意代码执行这一类的操作。但需要注意的是,有些库的实现依赖于这种反射技巧(例如ORM工具,AOP库等)。
我在回复的开头写道String对象并不是真的不可变,只是“看上去不可变”。这可能会误导读者以为String类的当前版本在访问限制方面有所疏忽,但事实上value数组使用了private和final修饰符。因此开发者需要注意:在Java中无法将数组声明为不可变的,即使使用了正确的访问修饰符,也不能将它暴露在类的外面。
鉴于这个话题如此火热,向各位推荐一些进阶读物:2009年JavaZone会议上Heinz
Kabutz发表的关于反射技术的疯狂演说,本文涵盖了反射操作中的常见问题,以及其他一些有关反射技术的内容。本文非常好,也非常疯狂。
本文揭示了为什么反射技术在某些场景下非常实用,但在大多数情况下,你应该避免使用它。
回答#2:
在Java中,如果两个String类型的变量被初始化为同一个字符串,那么这两个变量将被赋值以相同的对象引用。这就是表达式“Test1==Test2”返回值为true的原因。
![](http://cdn1.importnew.com/2014/02/83a4ebc5cce570ad779fb9e4f537ef3a.png)
Test3是由substring()方法创建的一个新的String对象,它并没有和Test1共享同一个value数组。(注:由于原作者笔误,下图中变量test1和test3的首字母并未大写,望读者注意)
我们可以通过反射技术访问String对象,获取到value数组的指针:
![](http://cdn1.importnew.com/2014/02/7e435976a8f0c6df127cbcc252928e83.png)
改变这个value数组的值便能够改变所有持有该数组指针的String对象的值,因此Test1和Test2的值都发生了改变。但由于Test3是由substring()方法创建的一个新的String对象,它的值并没有被改变。
![](http://cdn1.importnew.com/2014/02/56e061c2743578b2847bab9dcc6ba7ce.png)
原文链接: stackoverflow 翻译: ImportNew.com - 夏千林
译文链接: http://www.importnew.com/9468.html
众所周知Java中的String对象是不可变的,但我们来看下面这段代码:
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
为什么这段代码会是这样的运行结果?为什么s1和s2的值被改变了,但是s3的值却没有?
回答#1:
String对象是不可变的,但这仅意味着你无法通过调用它的公有方法来改变它的值。
上面的代码通过反射机制绕过了正常的API。采用这种方式,你还可以改变枚举的值,甚至可以改变Integer类型自动装箱时使用的查找表。
在这里,由于s1和s2指向同一个内部的字符串对象,因此他们的值都被改变了。正如其他回答所述,这是由编译器实现的。
s3没有被改变的原因确实令我诧异,我过去一直认为s3和s1共享同一个value数组(在Java
7u6之前的版本中的确是这样)。然而,通过查看String类的源码,我们可以看出子串对象的value数组是从原字符串对象中拷贝得到的(通过使用Arrays.copyOfRange(..)方法)。这是s3没有被改变的原因。
你可以安装一个SecurityManager来防止恶意代码执行这一类的操作。但需要注意的是,有些库的实现依赖于这种反射技巧(例如ORM工具,AOP库等)。
我在回复的开头写道String对象并不是真的不可变,只是“看上去不可变”。这可能会误导读者以为String类的当前版本在访问限制方面有所疏忽,但事实上value数组使用了private和final修饰符。因此开发者需要注意:在Java中无法将数组声明为不可变的,即使使用了正确的访问修饰符,也不能将它暴露在类的外面。
鉴于这个话题如此火热,向各位推荐一些进阶读物:2009年JavaZone会议上Heinz
Kabutz发表的关于反射技术的疯狂演说,本文涵盖了反射操作中的常见问题,以及其他一些有关反射技术的内容。本文非常好,也非常疯狂。
本文揭示了为什么反射技术在某些场景下非常实用,但在大多数情况下,你应该避免使用它。
回答#2:
在Java中,如果两个String类型的变量被初始化为同一个字符串,那么这两个变量将被赋值以相同的对象引用。这就是表达式“Test1==Test2”返回值为true的原因。
StringTest1="Hello World"; StringTest2="Hello World"; System.out.println(test1==test2);// true
![](http://cdn1.importnew.com/2014/02/83a4ebc5cce570ad779fb9e4f537ef3a.png)
Test3是由substring()方法创建的一个新的String对象,它并没有和Test1共享同一个value数组。(注:由于原作者笔误,下图中变量test1和test3的首字母并未大写,望读者注意)
我们可以通过反射技术访问String对象,获取到value数组的指针:
![](http://cdn1.importnew.com/2014/02/7e435976a8f0c6df127cbcc252928e83.png)
Field field = String.class.getDeclaredField("value"); field.setAccessible(true);
改变这个value数组的值便能够改变所有持有该数组指针的String对象的值,因此Test1和Test2的值都发生了改变。但由于Test3是由substring()方法创建的一个新的String对象,它的值并没有被改变。
![](http://cdn1.importnew.com/2014/02/56e061c2743578b2847bab9dcc6ba7ce.png)
原文链接: stackoverflow 翻译: ImportNew.com - 夏千林
译文链接: http://www.importnew.com/9468.html
相关文章推荐
- 为什么说Java的String对象是不可变的
- Java中的String对象是不可变的吗
- 为什么java中的String对象时不可变的
- Java中的String对象是不可变的吗?(jdk7u4测试)
- Java学习之String对象为什么是不可变的
- Java中的String对象为什么是不可变的?
- Java中的String对象是不可变的吗?
- java String不可变对象,但StringBuffer是可变对象
- 为什么说java的String对象是不可变的
- 为什么说Java的String对象是不可变的
- Java 中的 String对象为什么是不可变的
- java中将file文件对象转string
- JAVA面向对象 Part02 String常用Api , this static关键字
- Java String对象的经典问题(new String())
- Java中可变长参数String...的使用
- JAVA面试题解惑系列(二)——到底创建了几个String对象?
- 为什么Java的String对象不能改变
- 小白学java-基础篇(类、对象、String、this)
- Java String对象问题
- Java String对象使用方法详解