0x00 简介
又翻出来了在15年整理的笔记了。感觉当初还是挺较真的。
自己对String的理解总是存在着不同程度的误差,经常处于一知半解的状态,而且对其内部的原理也不是特别清楚,碰巧又和同学聊起这个知识点,秉承爱折腾的原则,在论文答辩之际详细整理一下。
0x01 说明
最初听说的String、StringBuffer和StringBuilder三者之间的区别主要是下面这个版本(略作总结):
String:字符串常量,字符串长度不可变。Java中String是immutable(不可变)的。用于存放字符的数组被声明为final的,因此只能赋值一次,不可再更改。
StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用StringBuffer,如果想转成String类型,可以调用StringBuffer的toString()方法。
StringBuilder:字符串变量(非线程安全)。在内部,StringBuilder对象被当作是一个包含字符序列的变长数组。
一般情况下,速度从快到慢:StringBuilder > StringBuffer > String。
0x02 详细分析
下面通过不同的角度来对这三个String相关的类型进行详细的分析和学习,主要通过源码以及反编译的字节码进行学习,另外对于常拿来比较三者之间性能的例子就不再重复了,整理下面内容的主要目标是深入理解这三者的区别。
下面将分别对这三者进行说明,都先从源码中观测一下其创建的过程,以及如何进行添加操作,随后对三个类型做同一种测试,即字符串的拼接,通过虚指令观测其虚拟机层面的执行原理,最后做一个总结。
String
源码
下面是String在jdk中的源码片段,可以看出,String类中实际存放数据是一个以final 类型的char数组,也就是说该数组是不可变的。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
public String() {
this.value = new char[0];
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
}
例子
简单代码
public class StringTest {
public static void main(String[] args) {
String s = "string ";
s += "is a good boy!";
s += "really?";
}
}
从下面的虚指令中可以看出,在往字符串s中添加新内容的时候,其过程在下面代码中注释。
[]$ javap -p -v StringTest
Classfile /home/hadoop/zhdd/StringTest.class
Last modified Dec 6, 2015; size 511 bytes
MD5 checksum f960070f295b4e22de6bffe8f06634f4
Compiled from "StringTest.java"
public class StringTest
SourceFile: "StringTest.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#19 // java/lang/Object."<init>":()V
#2 = String #20 // string
#3 = Class #21 // java/lang/StringBuilder
#4 = Methodref #3.#19 // java/lang/StringBuilder."<init>":()V<