ITEEDU

Java Gossip: 不可变的(immutable)字符串

一个字符串对象一旦被配置,它的内容就是固定不可变的(immutable),例如下面这个宣告:

String str = "caterpillar"; 

这个宣告会配置一个长度为11的字符串对象,您无法改变它的内容;别以为下面这个宣告就是改变一个字符串对象的内容:

String str = "just"; 
str = "justin"; 

事实上,在这个程序片段中,会有两个字符串对象,一个是"just",长度为4,一个是"justin",长度为6,它们两个是不同的字符串对象,您并不是在 "just"字符串后加上"in"字符串,而是让str名称参考至新的字符串对象,如下所示:

原来参考至此
str --------> "just"

重新指定后
"just" 等待回收

str ---------> "justin"
参考新的字符串对象

在Java中,使用 = 将一个字符串对象指定给一个名称,其意义为改变名称的参考对象,原来的字符串对象若没有其它名称来参考它,就会在适当的时机被Java的「垃圾回收」(Garbage collection)机制回收,在Java中,程序设计人员通常不用关心无用对象的资源释放问题,Java会检查对象是否不再被参考,如果没有任何名称参考的对象将会被回收。

如果您在程序中使用下面的方式来宣告,则实际上是指向同一个字符串对象:

String str1 = "flyweight";
String str2 = "flyweight"; 
System.out.println(str1 == str2);

程序的执行结果会显示true,在Java中,会维护一个String Pool,对于一些可以共享的字符串对象,会先在String Pool中查找是否存在相同的String内容(字符相同),如果有就直接传回,而不是直接创造一个新的String对象,以减少内存的耗用。

再来个一看例子,String的intern()方法,来看看它的API说明的节录:

Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

这段话其实说明了 Flyweight 模式 的运作方式,来用个实例来说明会更清楚:

StringIntern.java
public class StringIntern {
	public static void main(String[] args) {
		String str1 = "fly";
		String str2 = "weight";
		String str3 = "flyweight";
		String str4;
		str4 = str1 + str2;
		System.out.println(str3 == str4);
		str4 = (str1 + str2).intern();
		System.out.println(str3 == str4);
	}
}

在程序中第一次比较str3与str4对象是否为同一对象时,您知道结果会是false,而intern()方法会先检查 String Pool中是否存在字符部份相同的字符串对象,如果有的话就传回,由于程序中之前已经有"flyweight"字符串对象,intern()在String Pool中发现了它,所以直接传回,这时再进行比较,str3与str4所指向的其实是同一对象,所以结果会是true。

注意到了吗?== 运算在Java中被用来比较两个名称是否参考至同一对象,所以不可以用==来比较两个字符串的内容是否相同,例如:

String str1 = new String("caterpillar");
String str2 = new String("caterpillar");
System.out.println(str1 == str2);

上面会显示false的结果,因为str1与str2是分别参考至不同的字符串对象,如果要比较两个(字符串)对象是否相同,您要使用equals()方法,例如:

String str1 = new String("caterpillar");
String str2 = new String("caterpillar");
System.out.println(str1.equals(str2));

这样子结果才会显示所想要的比较结果:true。