String源码分析
一:实现接口。
public final class String
implements java.io.Serializable, Comparable
- java.io.Serializable
这个序列化接口没有任何方法和域,仅用于标识序列化的语意。
解读:这是一个里面什么都没有的接口;
如果一个接口里面什么内容都没有,那么这个接口是一个标识接口,比如,一个学生遇到一个问题,排错排了几天也没解决,此时,她举手了(示意我去帮他解决),然后我过去,帮他解决了,那么这个举手其实就是一个标识,自己不能解决的问题标示我去帮他解决,在Java中的这个Serializable接口是给JVM看的,告诉JVM,我不做这个类的序列化了,你(JVM)给我序列化,序列化就是变成二进制流,比如云计算、Hadoop,特别是Hadoop完全就是分布式环境,那么就要涉及到对象要在网络中传输,里面的全是二进制流,当然你来做这个序列化操作也可以,但是这个类里面可能还有一个类,如果你把外面的类对象Person变成二进制,那么里面也要序列化(这要用到深度遍历,很麻烦),干脆告诉JVM,让他来帮你做。
serializable接口就是Java提供用来进行高效率的异地共享实例对象的机制,实现这个接口即可。
序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
- Comparable
这个接口只有一个compareTo(T 0)接口,用于对两个实例化对象比较大小。
- CharSequence
这个接口是一个只读的字符序列。包括length(), charAt(int index), subSequence(int start, int end)这几个API接口,值得一提的是,StringBuffer和StringBuild也是实现了改接口。
可以看出 String 不变性的原因
final 修饰class,说明 String 类不能被继承,即 String 的方法,都不会被重写final 修饰 String 中保存数据的 char 的数组 value,value一旦被赋值,内存地址不可更改,而且 value 的权限是 private 的,外部绝对访问不到,String 也没有提供可以对 value 赋值的对外方法,所以 value 一旦产生,内存地址根本无法被修改。就是,充分利用了 final 的特性。因为 String 具有不变性,所以 String 的大多数操作方法,都会返回新的 String。
StringBuffer源码分析
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
...
}
StringBuffer类被 final 所修饰,因此不能被继承。
StringBuffer类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuilder也是继承于AbstractStringBuilder类。
StringBuffer类实现了2个接口:
- Serializable 序列化接口,表示对象可以被序列化。
- CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,比如:length()、charAt()、subSequence()、toString()方法等。
总结
StringBuffer类将所有操作字符序列的方法都添加了 synchronized 关键字来修饰,因此,StringBuffer类是线程安全的。
- StringBuffer类使用了一个char数组来存储字符。该数组是一个动态的数组,当存储容量不足时,会对它进行扩容。
- StringBuffer对象是一个可变的字符序列。
- StringBuffer类是线程安全的。
StringBuilder源码分析
StringBuilder类被 final 所修饰,因此不能被继承。
StringBuilder类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuffer也是继承于AbstractStringBuilder类。
StringBuilder类实现了2个接口:
- Serializable 序列化接口,表示对象可以被序列化。
- CharSequence 字符序列接口,提供了几个对字符序列进行只读访问的方法,比如:length()、charAt()、subSequence()、toString()方法等。
总结
- StringBuilder类使用了一个char数组来存储字符。该数组是一个动态的数组,当存储容量不足时,会对它进行扩容。
- StringBuilder对象是一个可变的字符序列。
- StringBuilder类是非线程安全的。
小结
三者的区别:
- StringBuffer和StringBuilder类型的字符串可以改变,且不会产生新的未使用字符串对象
- String类型的字符串不能在原字符串上做修改
- StringBuffer效率低,线程安全
- StringBuilder(JDK5提出)效率高,非线程安全
如何选择:
- 如果程序运行过程中不需要对字符串进行改变则使用String
- 多线程操作字符串缓冲区下操作大量数据则使用StringBuffer
- 单线程操作字符串缓冲区下操作大量数据则使用StringBuilder
一个小案例 证明StringBuffer和StringBuilder的线程安全问题
StringBuffer sbuf = new StringBuffer();
sbuf.append(1);
StringBuilder sbui = new StringBuilder();
sbui.append(1);
sbuf的append方法会调用:
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(int i) {
toStringCache = null;
super.append(i);
return this;
}
sbui的append方法会调用:
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(int i) {
super.append(i);
return this;
}
可以看到,StringBuffer类的append方法前加上了synchronized关键字保证了线程安全。但是这两个append方法最终调用的都是父类AbstractStringBuilder中的append方法:
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
下面通过Java多线程编程测试下StringBuffer和StringBuilder的线程安全问题:
public class RunnableTest implements Runnable {
public StringBuilder str = new StringBuilder("");
// public StringBuffer str = new StringBuffer("");
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
str.append(Thread.currentThread().getName());
System.out.println(str+"--"+str.length());
}
}
public class StringTest {
public static void main(String[] args) {
RunnableTest rt = new RunnableTest();
new Thread(rt, "aa").start();
new Thread(rt, "bb").start();
new Thread(rt, "cc").start();
new Thread(rt, "dd").start();
new Thread(rt, "ee").start();
new Thread(rt, "ff").start();
}
}
使用线程不安全的StringBuilder类时的结果,可以看到字符串的长度计算是有问题的,这就是因为字符串拼接和长度计算的代码块没有保障线程安全:
使用线程安全的StringBuffer类时的结果: