String:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence{}
被声明为 final class,除了hash这个属性其他属性都声明为final,因为它的不可变性,例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。
Java中为什么String类要设计成不可变的
在java中将String类设计成不可变的是综合考虑到各种因素的结果,需要综合内存、同步、数据结构、以及安全等方面的考虑。
1)字符串常量池的需要:字符串常量池是Java堆内存中一个特殊的存储区域,当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象,假若字符串对象允许改变,那么将会导致各种逻辑的错误,比如改变一个引用的字符串将会导致另一个引用出现脏数据。
2)允许String对象缓存HashCode:Java中String对象的哈希码被频繁的使用,比如在HashMap等容器中,字符串不变性保证了哈希码的唯一性,因此可以放心地进行缓存,这也是一种性能优化的手段,意味着不必每次都去计算新的哈希码。
安全性:String被许多Java类库用来当作参数,如:网络连接(network connection)、打开文件(opening files)等等,如果String不是不可变的,网络连接、打开文件将会被改变——这将导致一系列的安全威胁,操作的方法本以为连接上一台机器,其实不是,由于反射的参数都是字符串,同样也会引起一系列的安全问题。
StringBuffer:
public final classStringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
他是为了解决大量拼接字符串时产生的中间对象问题而提供的一个类。他是一个线程安全的可修改的字符序列,把所有的修改数据的方法都加上了 synchronized,但是性能是要付出一些代价的。
StringBuilder:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
很多情况下字符串拼接操作不需要线程安全的, StringBuilder本质上与StringBuffer没什么区别,去掉了synchronized保证线程安全的部分。减少了开销。
StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组),构造初始化没有字符时,初始容量为16个字符,如果有指定的字符串初始容量为字符串参数长度加上16个字符。
其实 String拼接用的是StringBuilder.append,也就是说有大量的字符串拼接new好多StringBuilder对象。
字符串常量池
java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。
创建一个字符串时,首先会检查字符串常量池里面有没有值相同的字符串对象,如果有就直接返回引用,不会创建字符串对象;如果没有则新建字符串对象,返回对象引用,并且将新创建字符串对象放入池中。但是通过new方法创建的String对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。
原则只适用于直接给String对象引用赋值的情况。
String str1 = new String("a"); //不检查字符串常量池的
String str2 = "bb"; //检查字符串常量池的
1、在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明,少量的字符串操作(拼接,删除等)。
2、在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String"+"来拼接而是使用,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。
3、在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。如HTTP参数解析和封装等。