jdk1.8 源码,三个类的类图如下
说明:
是否可变对象 | equals() 和hashcode() | 是否线程安全 | |
String | 不可变对象 | 重写 | |
StringBuffer | 可变对象 | 未重写 | 线程安全(synchronized修饰方法) |
StringBuilder | 线程不安全 |
首先明确什么不可变对象。一个对象在创建完成之后,不能再改变它的状态,那么这个对象是不可变对象。不能改变状态即不能改变对象内的成员。对于基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,且引用类型指向的对象的状态也不能改变。
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
}
从源码可以看出String类中使用字符数据保存字符串,private final char value[];
使用final修饰符,所有String对象是不可变。此外,final类是不可继承的的,所以String类不可继承。String中的对象是不可变的,也就可以理解为常量,显然线程安全。
重写了equals() 和hashcode() 方法,所以String比较时,比较的是内容。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,方法使用synchronized 修饰,所以是线程安全的。看如下源码:
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
private transient char[] toStringCache;
static final long serialVersionUID = 3388685877147921107L;
public synchronized int length() {
return count;
}
@Override
public synchronized char charAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
return value[index];
}
...
}
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{
@Override
public StringBuilder reverse() {
super.reverse();
return this;
}
}
它们的区别就在于一个关键字:synchronized。StringBuffer线程安全,StringBuilder线程不安全,所以决定了它们的使用场景,单线程和多线程。在单线程中,使用StringBuilder的效率要高,在多线程中,使用StringBuffer。
StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。StringBuilder和StringBuffer的初始大小都是16。
抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。
StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer会在方法上加synchronized关键字,进行同步。
5.1 CharSequence 接口
实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。提供这么一个接口,有些处理String或者StringBuffer的类就不用重载了。但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法
而这个接口中要强调的重点是:CharSequence并没有提取出IObject中的hashCode和equals方法的通用规范。所以对于2个不仅实现了CharSequence接口的对象(还继承了其他的类)的对象来说,在比较时,其结果也是未定义的。(因为对于继承了Object的类来说,根据具体的实现,比较时,是有2中选择的,比较地址用==,比较内容用equals。),每一对象都可以由不同的类来实现,不确定其是否具备同等比较的能力。因此,使用任意 的CharSequence实例作为Set集合的元素或者map中的key,都是不合适的,因为CharSequence实例没有equlas方法。
public interface CharSequence {
int length();
char charAt(int index);
CharSequence subSequence(int start, int end);
public String toString();
... //还有其他方法
}
5.2 Serializable接口
public interface Serializable {
}
在Serializable接口中,只是声明了一个接口,不包含任何变量或函数。这个接口的作用是实现对象的序列化与反序列化。
序列化:将对象转换为字节序列的过程称为序列化。
反序列化:将字节序列转换为对象的过程称为反序列化。
序列化与反序列化应用场景:对象持久化,网络中传输对象信息。