Java对比项(2) String&StringBuilder&StringBuffer
String & StringBuilder & StringBuffer
最熟悉的一集
Author: Corissa
Date: Dec.29 2023
重点需要掌握🏁:
理解 字符串 的设计,分类和实现
String 相关类的演进
JVM 对象缓存机制及相关优化
通过 String 学习基本的线程安全设计与实现
1 通用分析
String:
是 Java 中非常重要的类,提供了 构造和管理字符的基本逻辑。
String 是 Immutable 类,其类和属性都被声明为 final。
保存字符串的数组被
final
修饰且为private,并且String
类没有提供/暴露修改这个字符串的方法。
String
类被final
修饰导致其不能被继承,进而避免了子类破坏String
不可变。
由于其不可变性,在对 String 对象实例做拼接、裁剪等操作时都会产生新的 String 对象。当需要大量对字符串进行修改操作时,往往会影响到应用性能。
StringBuffer:
是字符串的可变类,且是线程安全的 (通过 synchronized 关键字实现)。本质上是一个线程安全的可修改字符串序列。
StringBuilder:
也是字符串的可变类,但是去掉了线程安全的部分,效率比StringBuffer高。
StringBuffer 和 StringBuilder 底层采用的都是 char[] (JDK以后是byte[])。二者都继承了 AbustractStringBuilder 类。
StringBuilder 和 StringBuffer 的初始长度都是初始字符串长度+16 (str.length() + 16)
2 拓展
2.1 String#equals() & Object#equals()
String 重写了 Object 中的 equals() 方法。在 Object 中,equals() 对于基本数据类型比较的是值,对于引用数据类型比较的是对象的内存地址。而 String 重写了 Object 中的 equals() 方法,比较的是 String 字符串的值。
2.2 字符串缓存
避免重复创建字符串,降低内存消耗和对象创建开销
Java 6
提供 intern() 方法,提示 JVM 把相应的字符串放到缓存当中去。当创建字符串对象并调用 intern() 方法时,如果已经有缓存,则返回缓存里的实例。
问题在于,Java 6 的字符串使唤存在 PermGen (永久代) 里的,空间有限,且不会被 FullGC 之外的垃圾收集照顾到,使用不当会产生 OOM 的问题。
且 intern() 是一种显示地排重机制,需要开发者显示地调用,且很难保证效率。
Java 8
将缓存放置在堆中,避免了永久占满的问题。永久代在 JDK 8 中也被 MetaSpace (元数据区)替代了。
在 JDK 8 中字符排重是 G1GC 下的字符排重,是使相同数据的字符串指向同一份数据,这是 JVM 底层的改变。
创建字符串常量池中没有的字符串过程是:
- 在堆中创建字符串对象
- 将该字符串对象的引用保存在字符串常量池中,以后有相同字符串直接返回
1
2
3
4
5
6// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true
2.3 String 演变史
底层
char[]
->byte[]
+编码coder
引入 Compact Strings 的设计
String 其实支持两个编码方案,Latin-1 和 UTF-16。大部分拉丁文用char
类型会造成一定的空间浪费,用byte
就能表示。对于一串字符串,如果其中的字符没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。byte
相较 char
节省一半的内存空间。
避免了一定程度的内存浪费提高了操作速度。
但是,在极端情况下,同样数组长度下存储能力退化了一倍。(相同长度的数组char是两字节)
3 Object 中的 hashcode() 与 equals()
hashCode()
定义在 Object
类中,也就是说任何类都包含有 hashCode()
函数。
另外需要注意的是:Object
的 hashCode()
方法是本地方法,也就是用 C 语言或 C++ 实现的。
hashCode
可以使容器的查找是否存在某元素的效率更高,比如HashMap
在查找key时,是对比 hashCode
来判断的。
当然,两个相等的hashCode
对象可能不同(发生了哈希碰撞)
重写equals()
必须重写hashCode()