目录
String & StringBuild & StringBuffer 总结
类型 | 特点 | 适用场景 |
---|---|---|
String | 不可变、线程安全 | 不需要操作数据或操作少量数据 |
StringBuild | 可变、线程不安全 | 需要频繁操作且不用考虑数据安全 |
StringBuffer | 可变、线程安全 | 需要频繁操作且需要考虑数据安全 |
String
String为什么不可变?
原因一:被 final
修饰,保证当前对象不能指向新的数组、不可被继承;
原因二:char value[]
被 private
修饰, 且 String
没有提供任何修改 value[]
的方法
不可变有什么好处?
好处一:实现 String
常量池(String pool
)
在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。
如果字符串是可变的,某一个字符串变量改变了其值,那么其指向的变量的值也会改变,String pool将不能够实现!
好处二:使多线程安全
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
好处三:加快字符串处理速度
由于String是不可变的,保证了hashCode的唯一性,于是在创建对象时其hashCode就可以放心的缓存了,不需要重新计算。这也就是一般将String作为Map的Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。
好处四:避免安全问题
如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
好处五:避免本地安全性问题
类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
不可变有什么缺点?
在需要处理大量字符串的情况下,每次修改字符串内容,都会创建一个新对象,从而导致性能低下。
StringBuilder & StringBuffer
StringBuilder
实现原理
StringBuilder
继承了 AbstractStringBuilder
,和 String
一样,AbstractStringBuilder
也同样使用 char value[]
保存字符串内容,但是没有被 final
和 private
修饰,保证内容可被访问且可变。
并且 StringBuilder
还提供了很多方法用于修改字符串(例如 append
)
缺点
由于是可变对象,存在线程安全问题
StringBuffer
实现原理
同样继承了 AbstractStringBuilder
,与 StringBuilder
的区别在于使用了 synchronized
关键字修饰字符串操作相关方法,从而保证线程安全
缺点
虽然是可变字符串,但由于每次操作字符串都需要加锁,性能相对较差
StringJoiner
上述可变字符串实现类在循环拼接字符串在使用分隔符拼接列表数据时存在一个问题:拼接结果末尾存在多余的分隔符
我们能够通过三种方式避免这个问题:
- 边界判断
- 拼接后删除末尾多余的分隔符
- 使用
StringJoiner
实现