在平常開發中,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;
}
複製代碼
首先,String
被final
所修飾,同時實現了Serializable
,Comparable
和CharSequence
接口。咱們一個個來看:數組
根據源碼,咱們能夠看到String
的值在內部是用一個byte
數組value
維護的,因此value
是一個引用類型,對於引用類型的變量,咱們每每都會十分當心,由於鬼知道在何時,咱們就會掉進一個大坑... 更況且String
是一個很是經常使用的類。因此一個很是重要的緣由就是安全性,使得String
類須要被final
修飾,即不可變。因此對String
類加上了final
修飾符,防止String
被繼承,從而重寫value
。剛纔說到value
是一個引用類型,因此在value
上也加上了final
修飾,而且在整個類中,沒有對value
進行update的操做。全部的update操做都是會新建一個新的string
。緩存
詳情可見🔎「OpenJdk-11 源碼-系列」Serializable安全
先來看看Comparable
接口app
public interface Comparable<T> {
public int compareTo(T o);
}
複製代碼
Comparable
接口是一個排序接口,若一個類實現了該接口並重寫了compareTo
方法,就意味着這個類支持了排序。當存在一個實現了Comparable
接口的類的集合或數組,那麼該集合或數組就能夠經過Collections.sort / Arrays.sort
進行排序。函數
a.compareTo(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
方法的時候,若是value
是LATIN1
編碼的話那麼就會右移一位,減小一個字節數。
hash
hash
是String
在實例化的時候對hashcode
的一個緩存。因爲在開發中String
會常常拿來比較,好比HashMap
中若是key
是String
類型,每次比較若是都從新計算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 str = "this is";
String str1 = str + "str"
複製代碼
在底層,Java 對 String 的+的支持使用的是 StringBuilder
以及它的append
和toString
方法。即:
String str = "str";
String str1 = (new StringBuilder(String.valueOf(str))).append("str").toString();
複製代碼
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
複製代碼