本文最后更新于7 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com
为什么String不可变
- final类:String类被final修饰,意味着它不能被继承
- private访问权限:存储字符的value数组是private的,外部代码无法直接访问到这个数组。
- final数组引用:value引用被final修饰,意味着这个引用一旦被赋值,就不能再指向另一个数组
注意:final修饰的是引用,而不是数组本身。可以通过某种方式拿到数据,并且可以修改元素。
主要有以下几点原因:
1.线程安全。不可变对象天生就是线程安全的。因为它们的状态不可变,所以可以被多个线程共享而无需任何同步机制。这极大地提升了性能并降低了复杂性。
2.作为哈希表的键。
String是HashMap、HashSet等集合最常用的键。
- 对象的
hashCode()方法在String中是这样计算的:它遍历value数组中的字符来计算哈希值。由于字符串不可变,所以哈希值在创建时计算一次并缓存下来(见源码中的private int hash字段),之后每次调用hashCode()都直接返回缓存值,效率极高。
- 如果
String可变,那么修改字符串会导致其哈希值改变,这在HashMap中会造成灾难性后果(对象再也找不到了)。
3.出于安全考虑。网络地址URL、文件路径path、密码通常情况下都是以String类型保存,假若String不是固定不变的,将会引起各种安全隐患。比如将密码用String的类型保存,那么它将一直留在内存中,知道垃圾收集把它清除。假如String类不是固定不变的,那么这个密码可能会被改变,导致出现安全隐患。
4.字符串常量池优化。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
String、StringBuffer和StringBuilder区别
1.可变性
- String不可变
- StringBuffer和StringBuilder可变
2.线程安全
- String不可变,因此是线程安全的
- StringBudiler不是线程安全的
- StringBuffer是线程安全的,内部使用synchronied进行同步
总结与使用建议
- 操作少量的数据或字符串内容不常改变:使用String。
- 在单线程环境下操作大量字符串数据(如在循环中拼接):使用StringBuilder。
- 在多线程环境下操作大量字符串数据:使用StringBuffer。
一个简单的性能比较:StringBuilder>StringBuffer>String(在频繁修改的场景下)
String类的常用方法有哪些?
- indexOf():返回指定字符的索引
- charAt():返回指定索引处的字符
- replace():字符串替换
- trim():去除字符串两端空白
- split():分割字符串,返回一个分割后的字符串数组
- getBytes():返回字符串的byte类型数组
- length():返回字符串长度
- toLowerCase():将字符串转成小写字符
- toUpperCase():将字符串转成大写字符
- substring:截取字符串
- equals():字符串比较



