前言
最近公司招聘Java开发人员,面试时会问一些jdk源码相关的问题,如String相关,但很少有答得全面的,今天总结一下,供学习参考
String,StringBuffer,StringBuilder之间的对比:
特性 | String | StringBuffer | StringBuilder |
---|---|---|---|
可继承性 | 类由final修饰,不能被继承 | 类由final修饰,不能被继承 | 类由final修饰,不能被继承 |
底层结构 | 由final修饰的char[] | 可变的char[] | 可变的char[] |
默认长度 | 无 | 16 | 16 |
扩容机制 | 无 | 父类中的方法expandCapacity进行扩容实现, 检测到数组长度不够容纳新元素时,进行扩容,新容量大小=value.length * 2 + 2, 若还不够, 则直接扩到需要的大小 | 同StringBuffer一样 |
实现的接口 | Serializable, Comparable, CharSequence | Serializable, CharSequence | Serializable, CharSequence |
继承的类 | AbstractStringBuilder, 很多操作都是调用父类中的方法 | AbstractStringBuilder, 很多操作都是调用父类中的方法 | |
线程安全性 | 线程安全,内部方法实现都由synchronized修饰 | 线程非安全 | |
适用场景 | 要操作少量的数据,较少更改 | 多线程环境下操作大量字符数据 | 单线程环境下操作大量字符数据 |
hashCode的实现 | 重写了hashCode | 未重写,调用Object的native方法hashCode | 同StringBuffer一样 |
equals的实现 | 重写了equals方法,比较的值是否相等 | 未重写,调用Object的方法equals,使用==比较,比较的是内存地址是否相等 | 同StringBuffer一样 |
序列化的实现 | 无 | 内部定义了writeObject和readObject, 序列化及反序列化时会调用应对的方法, 主要实现value及count的写入 | 同StringBuffer一样 |
总结:
-
三者的继承结构(这里忽略了Serializable)
-
StringBuffer与StringBuilder两者之间的主要区别
(1)线程安全性,StringBuffer是线程安全的,而StringBuilder是线程非安全的,也因此在速度上, StringBuilder优于StringBuffer
(2)还有一点,StringBuffer内部有针对toString()的cache, 字段名toStringCache,StringBuffer每次操作都会将toStringCache设置为null -
性能,速度
使用StringBuffer与StringBuilder时,注意初始化长度的设定,避免出现频繁扩容而导致的资源浪费
大多数情况下:StringBuilder > StringBuffer > String
单字符串操作时, String最佳, 如下:
String s1 = "abc";
StringBuilder s2 = new StringBuilder().append("abc");
- 使用哪一个
有大量字符串拼接或修改时, 单线程环境下用StringBuilder, 多线程环境下用StringBuffer, 若使用String进行大量字符拼接操作, 则会产生大量垃圾对象,浪费系统资源并且会影响系统性能
不涉及字符拼接以及更改时,直接使用String最佳
源码赏析:
- StringBuffer与StringBuilder序列化的实现
StringBuffer的实现:
/**
* readObject is called to restore the state of the StringBuffer from
* a stream.
*/
private synchronized void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
java.io.ObjectOutputStream.PutField fields = s.putFields();
fields.put("value", value);
fields.put("count", count);
fields.put("shared", false);
s.writeFields();
}
/**
* readObject is called to restore the state of the StringBuffer from
* a stream.
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
java.io.ObjectInputStream.GetField fields = s.readFields();
value = (char[])fields.get("value", null);
count = fields.get("count", 0);
}
StringBuilder的实现:
/**
* Save the state of the {@code StringBuilder} instance to a stream
* (that is, serialize it).
*
* @serialData the number of characters currently stored in the string
* builder ({@code int}), followed by the characters in the
* string builder ({@code char[]}). The length of the
* {@code char} array may be greater than the number of
* characters currently stored in the string builder, in which
* case extra characters are ignored.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
s.defaultWriteObject();
s.writeInt(count);
s.writeObject(value);
}
/**
* readObject is called to restore the state of the StringBuffer from
* a stream.
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
count = s.readInt();
value = (char[]) s.readObject();
}
- StringBuffer与StringBuilder的扩容检测及实现
AbstractStringBuilder类中的方法
//扩容检测
private void ensureCapacityInternal(int minimumCapacity) {
//数组长度不够容纳新元素时,进行扩容
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
//扩容实现
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
//开辟一个新数组, 并且原数组的元素复制过去
value = Arrays.copyOf(value, newCapacity);
}
- String类重写的hashCode方法实现
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}