学习 Java 的数据类型时,特殊关照了 String 类型,具有不变性。
当时没有深入思考为什么 String 有不变性。而且我只碰到了 String 才有不变性。Java这样设计的缘由何在。
突然在想,为什么 Struts2 中的 action 的返回值为什么也是 String 类型的,不是其他类型,SpringMVC中 handle 也是返回的是 String 类型【ModelAndView 里面也包含了视图名的字符串形式,void就不提了,处理 AJAX 】。
下面就好好分析这个字符串不变性。
所谓不变性就是一旦创建一个字符串对象,这个字符串对象的内容就不会被改变了。比如:
package com.msym.test; public class Test {
public void show() {
String str1 = "aaa";
String str2 = "aaa";
String str3 = new String("aaa");
System.out.println("str1的hashCode: " + str1.hashCode());
System.out.println("str2的hashCode: " + str2.hashCode());
System.out.println("str3的hashCode: " + str3.hashCode()); str1 = "aaa" + "b";
System.out.println("str1的hashCode: " + str1.hashCode());
System.out.println("str2的hashCode: " + str2.hashCode());
}
@org.junit.Test
public void test(){
show();
}
}打印出来的结果说明,str1,str2,str3是指向同一块内存空间的,当修改了 str1时,就创建了一个新的字符串了。
上面创建 str1,str2,str3:
创建 str1 时,常量池里面肯定是没有常量“aaa”,所以会创建出一个,但是当创建 str2时,因为之前已经创建过 “aaa”了,所以会直接指向这个“aaa”;创建 str3 时,尽管使用了 new 关键字,但是还是会先去常量池去搜寻“aaa”,如果存在,就只在堆内存中开辟一段空间,用以存放常量池中“aaa”的地址,如果不存在,那么就先在常量池中创建“aaa”,然后在堆中开辟空间,存储常量池中“aaa”的地址。
下面画张图来说明:【其中的 0X1122 是常量池中“aaa”的地址头,】
而且 String 类的任何方法都不是去修改字符串的值,而是创建了一个新的字符串,原来的字符串如果没有被引用的话,会在一段时间内被 gc 掉,这样貌似看来,这个不变性并没有带来什么好处,反倒浪费了不少内存,但是这样的机制正好有了安全性,只要创建出一个 String 对象,只要他没有被 gc ,任何方法来读取这个对象都会是同一个,不会出现两个一样的,这也是为什么 String 类型在网络连接,数据传递,以及各大框架使用的这么频繁。
当然,如果只是想使用 String类的功能,有更好的选择,那就是线程安全的 StringBuffer和线程不安全的StringBuilder。
1