Java中String为什么要设计成final呢?
首先我们来写一个测试类:
package someTest;
public class String_test {
public static String appendStr(String s) {
s+="bbb";
return s;
}
public static StringBuilder appendSb(StringBuilder sb) {
return sb.append("bbb");
}
public static void main(String[] args) {
String string=new String("aaa");
String nString=appendStr(string);
System.out.println("nString is:"+nString.toString());
System.out.println("string is:"+string.toString());
StringBuilder sBuilder=new StringBuilder("aaa");
StringBuilder nStringBuilder=appendSb(sBuilder);
System.out.println("nStringBuilder is:"+nStringBuilder.toString());
System.out.println("sBuilder is:"+sBuilder.toString());
}
}
我们来看看输出的结果:
nString is:aaabbb
string is:aaa
nStringBuilder is:aaabbb
sBuilder is:aaabbb
以上我们会发现String类区别于StringBuilder的一些不同点。
从中我们可以体会到String不可变的安全性
下面我们来看个比较严重的问题:
老规矩,上一段代码:
package com.cqu.someTest1;
import java.util.HashSet;
public class StringBuilder_test {
public static void main(String[] args) {
HashSet<StringBuilder> hSet=new HashSet<StringBuilder>();
StringBuilder sb1=new StringBuilder("aaa");
StringBuilder sb2=new StringBuilder("aaabbb");
hSet.add(sb1);
hSet.add(sb2);
StringBuilder sb3=sb1;
sb3.append("bbb");
System.out.println(hSet);
}
}
输出结果就是:
[aaabbb, aaabbb]
能看出什么问题吗,首先,StringBuilder型变量sb1和sb2分别指向堆内存当中的"aaa"和"aaabbb",将它们都插入到HashSet当中,到这里都没有问题。可之后sb3也指向了sb1的地址,StringBuilder没有不可变性的保护,sb3修改了"aaa",那么sb1也跟着就被篡改了。导致HashSet当中出现了两个"aaabbb",这很明显就违背了hashset键值的唯一性。
其次让我们来看看Java核心卷当中的一句话:
将方法或类声明为final主要目的是:确保它们不会再子类中改变语义。String类是final类,这意味着不允许任何人定义String的子类。换言之,如果有一个String的引用,它引用的一定是一个String对象,而不可能是其他类的对象。
另外,让我们来看看另外一段直观的解释:
1、线程安全。基本类型传值对象传引用,记住这一点。既然传引用,两个变量就有可能指向同一个String。而在String可变情况下,我要是通过一个变量来更改String,那么另一个变量取到的String也就变了!为什么?因为两个变量指向同个String。试想在多线程里,这会是多么可怕的一件事情:一个线程正在处理一个String,发现自己处理的String竟然变了。惊了?!shenmegui?!而且,因为多线程由CPU调度的关系,你难以确定到底这个可变的String会变成shenmegui。啥?你说封装在线程安全的类里?直接让他不可变多省事。
2、小明写了个方法:
String method(String origin){
origin += origin.substring(2);
return origin;}
然后他将同个字符串分别传入两次并打印:
Shenmeguienmegui
Shenmeguienmeguienmeguienmegui
小明惊了?!随即他发现,因为String是可变的,而第一次调用就已经改变了原来的字符串。所以……
String method(String origin){
String result = origin.clone();
result += result.substring(2);
return result;}
一边骂着Moon的工程师干嘛不让字符串不可变,小明终于得到了他要的输出:
Shenmeguienmegui
Shenmeguienmegui
幸好这个世界的Sun公司让String不可变,感谢Sun机智的工程师帮我们省了很多事。