字符串

Wu Jun 2020-01-02 09:43:49
Categories: > > Tags:

1 String 底层实现

String 被声明为 final,因此它不可被继承。

底层是 char 或 byte 类型的 value 数组,value 数组也被声明为 final,这意味着数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

编辑器可让字符串共享在常量池。

1.1 Java 8 - char 数组

在 Java 8 中,String 内部使用 char 数组存储数据。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}

1.2 Java 9 - byte 数组

在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final byte[] value;

    /** The identifier of the encoding used to encode the bytes in {@code value}. */
    private final byte coder;
}

1.3 不可变的好处

2 StringBuilder 和 StringBuffer

2.1 可变性与线程安全

2.2 底层实现

StringBuilder 和 StringBuffer 继承了 AbstractStringBuilder,AbstractStringBuilder 的 char 数组没有 final 关键字修饰,字符数组长度可变,所有 StringBuilder 和 StringBuffer 也是可变的

AbstractStringBuilder

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

StringBuilder

public StringBuilder() {
    // StringBuilder 类继承 AbstractStringBuilder 抽象类
    // 创建长度 16 的字符数组
    super(16);
}
// 字符串拼接
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

3 String Pool 与引用

字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。

在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

String s1 = "Programming";//先去常量池取,没有就新建对象放在常量池
String s2 = new String("Programming");//两个字符串对象,一个是常量池的"Programming",一个是用 new 创建在堆上的对象
String s3 = "Program";//常量池
String s4 = "ming";//常量池
String s5 = "Program" + "ming";//常量池
String s6 = s3 + s4;//堆
System.out.println(s1 == s2);//false
System.out.println(s1 == s5);//true
System.out.println(s1 == s6);//false
System.out.println(s1 == s6.intern());//true
System.out.println(s2 == s2.intern());//false

4 String 常用方法

4.1 初始化

1)使用字符串常量直接初始化
String s = "hello!";
2)使用构造方法创建并初始化
String s = new String(Object);

初始化源码

private final char value[];
// 本质是字符数组常量,所以不可变
public String() {
    this.value = "".value;
}

4.2 操作

1)截取字符串
2)拼接字符串

字符串的 + 操作其本质是创建了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对象用 toString 方法处理成 String 对象

一般情况进行字符串拼接用 + 就可以,但是如果是循环拼接,则需要用 StringBuilder 的 append 来实现。

若不使用 StringBuilder 的 append 方法而使用 + 来进行连接。那么每次在循环体内都将会在 Heap 中创造一个新的 String 对象,造成资源浪费。

3)获取信息
4)替换字符串
5)判断字符串
6)字符串转换