Java技术——String类为什么是不可变的

0. 前言  

如果一个对象,在它创建完成之后不能再改变它的状态,包括对象内的成员变量、基本数据类型的值等等。那么这个对象就是不可变的。众所周知String类就是不可变的。转载请注明出处为SEU_Calvin的博客。

 

1. String类为什么是不可变的 

首先看一下String类的源码中:

//JDK1.6
public final class String  
    implements java.io.Serializable, Comparable<String>, CharSequence  
{  
    /**用于存储字符的数组 */  
    private final char value[];  
    /** 表示String在这个value数组中的起始位置 */  
    private final int offset;  
    /** 字符个数*/  
    private final int count;  
    /** 哈希值 */  
    private int hash; // Default to 0  
  //...
}

//JDK1.7
public final class String  
    implements java.io.Serializable, Comparable<String>, CharSequence {  
    /**用于存储字符的数组 */  
    private final char value[];  
    /** 哈希值 */  
    private int hash; // Default to 0  
  //....
}

无论是JDK6还是7String类都是对字符数组的封装(而且value也只是一个引用,它指向一个真正的数组对象)。源码中并没有提供valueset方法,因此String类一旦初始化,外部便无法修改,同时value被修饰为 private final,在String类内部也无法改变。所以String对象是不可变的。

我们常用的字符串方法比如substringreplacereplaceAlltoLowerCase方法,给人的感觉好像是可以改变String内的值,但是这些方法的内部其实在完成逻辑后创建了一个新的String对象并返回。以replace为例,以下是一个简单的例子证明这一点。

String s = "DF2lian";  
s = s.replace('F', 'A');  
System.out.println("s = " + s);  //输出DA2lian

String s = "DF2lian";  
s.replace('F', 'A');  
System.out.println("s = " + s);  //输出DF2lian


2. String类真的不可变吗 

从上文可知String的成员变量valueprivate final修饰的,初始化后不可再改变并且不能再指向其他数组对象。但是value本身是一个引用变量,而不是真正的对象。那么我们就可以通过反射得到String对象中的value属性,进而改变value引用的数组的结构。下面是实例代码:

String s = "DF2lian";   
//获取String类中的value字段  
Field valueFieldOfString = String.class.getDeclaredField("value");  
//改变value属性的访问权限  
valueFieldOfString.setAccessible(true);  
//获取s对象上的value属性的值  
char[] value = (char[]) valueFieldOfString.get(s);  
//改变value所引用的数组中某个字符  
value[1] = 'R';  
System.out.println("s = " + s);  //输出DR2lian

你可能感兴趣的:(Java技术——String类为什么是不可变的)