String为什么要设计成不可变?
String是不可变类有以下几个优点:
- 由于String是不可变类,所以天然在多线程中使用是安全的,不需要做任何其他同步操作。
- 字符串不可变,可以在java运行时节省大量java堆空间。因为不同的字符串变量可以引用池中的相同的字符串。如果字符串是可变的话,任何一个变量的值改变,就会反射到其他变量,那字符串池也就没有任何意义了。
此时或许有人想到Integer也是不可变的,但在多线程操作中,仍需考虑同步问题,其实先吃安全的解释很简单,如下代码
String a = "A";
new Thread(new Runnable() {
@Override
public void run() {
String b = "A";
b = "B";
}
});
多线程中修改了b对象,将“A”改成了“B”,由于“A”存在常量池中,如果“A”可变,会导致上面对象a的值修改。那么字符串常量池就没有意义。同理,多线程修改的只是引用重新指到了新的常量池地址上,不会导致线程不安全,不可变是天然线程安全。
String的效率一定比StringBuilder低吗?
首先了解一下String、StringBuilder、StringBuffer区别。
String是不可变类,每当我们对String进行操作的时候,总是会创建新的字符串。操作String很耗资源,所以Java提供了两个工具类来操作String – StringBuffer和StringBuilder。
StringBuffer和StringBuilder是可变类;StringBuffer是线程安全的,StringBuilder则不是线程安全的。所以在多线程对同一个字符串操作的时候,我们应该选择用StringBuffer。由于不需要处理多线程的情况,StringBuilder的效率比StringBuffer高。
StringBuffer是通过synchronized关键字做同步的,源码截取如下
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
* @see #length()
*/
@Override
public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
toStringCache = null;
value[index] = ch;
}
String和StringBuilder效率验证
public class StringTest {
public static void main(String[] args) {
// String效率高
String a = "A" + "B";
StringBuilder b = new StringBuilder("A").append("B");
// 效率基本一样
String c = "A";
String d = "B";
String e = c + d;
StringBuilder builder = new StringBuilder(c);
StringBuilder append = builder.append(d);
}
}
String 对象是使用最频繁的对象之一,对它的优化一直没有停止过,包括编译阶段。字符串,我们经常用“+”连接,针对这种情况,编译器是可以进行优化的, “+”连接形式创建字符串分两种情况 :
1 使用常量的字符串连接创建是也是常量,编译期就能确定了,直接入字符串常量池,比如 String str1 = “ab” + “cd”, “ab” 和 “cd” 都是常量,编译器优化后的代码为:String str1 = “abcd”。( 注意:final 修饰的变量如果是常量,可以认为等同于字面量 )
2 使用“+”连接字符串中含有变量时,这种情况在运行期才能确定。
我们通过javap反编译查看字节码,证明了String的优化内容
