「OpenJdk-11 源碼-系列」 : String

類的定義

在平常開發中,String能夠說是最經常使用的類之一了,但也是最容易被忽視的類。先來看看String的定義java

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {

	private final char[] value; // jdk 8
    private final byte[] value; // jdk 9 使用 byte 減小空間的浪費,

    private final byte coder;
    private int hash; // Default to 0
    private static final long serialVersionUID = -6849794470754667710L;
}
複製代碼

首先,Stringfinal所修飾,同時實現了SerializableComparableCharSequence接口。咱們一個個來看:數組

爲撒要用 final 修飾

根據源碼,咱們能夠看到String的值在內部是用一個byte數組value維護的,因此value是一個引用類型,對於引用類型的變量,咱們每每都會十分當心,由於鬼知道在何時,咱們就會掉進一個大坑... 更況且String是一個很是經常使用的類。因此一個很是重要的緣由就是安全性,使得String類須要被final修飾,即不可變。因此對String類加上了final修飾符,防止String被繼承,從而重寫value。剛纔說到value是一個引用類型,因此在value上也加上了final修飾,而且在整個類中,沒有對value進行update的操做。全部的update操做都是會新建一個新的string緩存

爲撒實現 Serializable 接口

詳情可見🔎「OpenJdk-11 源碼-系列」Serializable安全

Comparable & Comparator

先來看看Comparable接口app

public interface Comparable<T> {
    public int compareTo(T o);
}
複製代碼

Comparable接口是一個排序接口,若一個類實現了該接口並重寫了compareTo方法,就意味着這個類支持了排序。當存在一個實現了Comparable接口的類的集合或數組,那麼該集合或數組就能夠經過Collections.sort / Arrays.sort進行排序。函數

  • 當返回值爲正數,那麼就意味着 a > b (a.compareTo(b))
  • 當返回值爲零,那麼a = b
  • 當返回值爲負數,那麼a < b

再來看看Comparator 接口post

public interface Comparator<T> {
    //最主要的倆方法
    int compare(T o1, T o2);
    boolean equals(Object obj);
}
複製代碼

剛纔咱們說到若是一個類實現了Comparable接口後,那麼該類就有了排序的功能。但有的時候咱們不想修改這個類,減小對類的侵入。那麼咱們就能夠自定義一個類來實現Comparator接口。ui

總結一下,實現Comparable接口能夠作到內部比較(即相同類之間的比較);而實現了Comparator接口的類,可使用該類對任意兩個類型的對象進行比較,且無侵入性。this

屬性

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final byte[] value;
    private final byte coder;
    private int hash; // Default to 0

}
複製代碼
  • value編碼

    value存儲的是 String的內容,即當使用String str = "abc";的時候,"abc"是存儲在一個byte類型的數組中的。這在後續的構造函數中會講解到。

  • coder

    在JDK9中,String維護了這樣一個新的屬性coder,它是一個編碼格式的標識,表示LATIN1或者UTF-16,String生成的時候會自動初始化這個值,若是字符串中都是能用LATIN1就能表示的就是0,不然就是UTF-16。

    那麼爲何要加這個coder屬性呢?先說結論,能夠對字符串的空間進行壓縮。先看源碼

    //jdk8
        public int length() {
            return value.length;
        }
    //jdk9
        static final boolean COMPACT_STRINGS;
        static {
            COMPACT_STRINGS = true; //默認開啓壓縮
        }    
        byte coder() {
            return COMPACT_STRINGS ? coder : UTF16;
        }
        public int length() {
            return value.length >> coder(); //若是coder() = 1則會右移一位
        }
    複製代碼

    因此,若是當咱們調用length方法的時候,若是valueLATIN1編碼的話那麼就會右移一位,減小一個字節數。

  • hash

    hashString在實例化的時候對hashcode的一個緩存。因爲在開發中String會常常拿來比較,好比HashMap中若是keyString類型,每次比較若是都從新計算hashcode的話就會很費時。

構造方法

public String() {
        this.value = "".value;
        this.coder = "".coder;
    }
複製代碼

空參構造方法,會建立一個空的字符串序列。通常不會這麼建立。

//常見的幾種構造方法
	public String(byte[] bytes) {
        this(bytes, 0, bytes.length);
    }
	public String(byte bytes[], String charsetName) throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
	public String(byte bytes[], Charset charset) {
        this(bytes, 0, bytes.length, charset);
    }
    public String(byte bytes[], String charsetName) throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
    
    public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        checkBoundsOffCount(offset, length, bytes.length);
        StringCoding.Result ret =
            StringCoding.decode(charsetName, bytes, offset, length);
        this.value = ret.value;
        this.coder = ret.coder;
    }
	
	public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBoundsOffCount(offset, length, bytes.length);
        StringCoding.Result ret =
            StringCoding.decode(charset, bytes, offset, length);
        this.value = ret.value;
        this.coder = ret.coder;
    }
複製代碼

使用字節數組來建立能夠分爲指定編碼或默認編碼(ISO-8859-1)進行編碼操做。

經常使用方法

比較簡單,可直接查看API

其它

String 對 + 號的重載

String str = "this is";
String str1 = str + "str"
複製代碼

在底層,Java 對 String 的+的支持使用的是 StringBuilder 以及它的appendtoString方法。即:

String str = "str";
String str1 = (new StringBuilder(String.valueOf(str))).append("str").toString();
複製代碼

intern

public native String intern();
複製代碼

能夠看到 intern 是被 native修飾,這說明該方法是由底層的C/C++進行實現的。它的做用是 若是常量池中存在當前字符串, 就會直接返回當前字符串。若是常量池中沒有此字符串, 會將此字符串放入常量池中後, 再返回。經過雙引號聲明的字符串會放入到常量池中

String str = "123";
String str1 = new String("123");
String str2 = str1.intern();
s
System.out.println(str == str1); //false
System.out.println(str == str2); // true
複製代碼
相關文章
相關標籤/搜索