转载:http://blog.****.net/zhangjg_blog/article/details/18319521
什么是不可变对象?
众所周知, 在Java中, String类是不可变的。那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。
区分对象和对象的引用
- String s = "ABCabc";
- System.out.println("s = " + s);
- s = "123456";
- System.out.println("s = " + s);
打印结果为:
s = 123456
为什么String对象是不可变的?
- public final class String
- implements java.io.Serializable, Comparable<String>, CharSequence
- {
- /** The value is used for character storage. */
- private final char value[];
- /** The offset is the first index of the storage that is used. */
- private final int offset;
- /** The count is the number of characters in the String. */
- private final int count;
- /** Cache the hash code for the string */
- private int hash; // Default to 0
在JDK1.7中,String类做了一些改动,主要是改变了substring方法执行时的行为,这和本文的主题不相关。JDK1.7中String类的主要成员变量就剩下了两个:
- public final class String
- implements java.io.Serializable, Comparable<String>, CharSequence {
- /** The value is used for character storage. */
- private final char value[];
- /** Cache the hash code for the string */
- private int hash; // Default to 0
由以上的代码可以看出, 在Java中String类其实就是对字符数组的封装。JDK6中, value是String封装的数组,offset是String在这个value数组中的起始位置,count是String所占的字符的个数。在JDK7中,只有一个value变量,也就是value中的所有字符都是属于String这个对象的。这个改变不影响本文的讨论。 除此之外还有一个hash成员变量,是该String对象的哈希值的缓存,这个成员变量也和本文的讨论无关。在Java中,数组也是对象(可以参考我之前的文章java中数组的特性)。 所以value也只是一个引用,它指向一个真正的数组对象。其实执行了String s = “ABCabc”; 这句代码之后,真正的内存布局应该是这样的:
- String a = "ABCabc";
- System.out.println("a = " + a);
- a = a.replace('A', 'a');
- System.out.println("a = " + a);
打印结果为:
a = aBCabc
- String ss = "123456";
- System.out.println("ss = " + ss);
- ss.replace('1', '0');
- System.out.println("ss = " + ss);
打印结果:
ss = 123456
String对象真的不可变吗?
- public static void testReflection() throws Exception {
- //创建字符串"Hello World", 并赋给引用s
- String s = "Hello World";
- System.out.println("s = " + s); //Hello World
- //获取String类中的value字段
- Field valueFieldOfString = String.class.getDeclaredField("value");
- //改变value属性的访问权限
- valueFieldOfString.setAccessible(true);
- //获取s对象上的value属性的值
- char[] value = (char[]) valueFieldOfString.get(s);
- //改变value所引用的数组中的第5个字符
- value[5] = '_';
- System.out.println("s = " + s); //Hello_World
- }
打印结果为:
s = Hello_World