String是java中很是經常使用的一個對象類型。能夠說java中使用最多的就是String了。那麼String到底有哪些祕密呢?接下來本文將會一一講解。java
String是不可變的,官方的說法叫作immutable或者constant。數組
String的底層實際上是一個Char的數組。安全
private final char value[];
全部的String字面量好比"abc"都是String的實現。函數
考慮下面的賦值操做:this
String a="abc"; String b="abc";
對於java虛擬機來講,"abc"是字符串字面量,在JDK 7以後,這個字符串字面量是存儲在java heap中的。而在JDK 7以前是有個專門的方法區來存儲的。spa
有了「abc」,而後咱們將「abc」 賦值給a和b。線程
能夠看到這裏a和b只是java heap中字符串的引用。code
再看看下面的代碼發生了什麼:對象
String c= new String("abc");
首先在java heap中建立了「abc」,而後調用String的構造函數:blog
public String(String original) { this.value = original.value; this.hash = original.hash; }
在構造函數中,String將底層的字符串數組賦值給value。
由於Array的賦值只是引用的賦值,因此上述new操做並不會產生新的字符串字面值。
可是new操做新建立了一個String對象,並將其賦值給了c。
String的不可變性還在於,String的全部操做都會產生新的字符串字面量。原來的字符串是永遠不會變化的。
字符串不變的好處就在於,它是線程安全的。任何線程均可以很安全的讀取字符串。
一直以來,java開發者都有這樣的問題,java究竟是傳值仍是傳引用呢?
我想,這個問題能夠從兩方面來考慮。
首先對於基礎類型int,long,double來講,對他們的賦值是值的拷貝。而對於對象來講,賦值操做是引用。
另外一方面,在方法調用的參數中,所有都是傳值操做。
public static void main(String[] args) { String x = new String("ab"); change(x); System.out.println(x); } public static void change(String x) { x = "cd"; }
咱們看上面的例子,上面的例子輸出ab。由於x是對「ab」的引用,可是在change方法中,由於是傳值調用,因此會建立一個新的x,其值是「ab」的引用地址。當x被從新賦值以後,改變的只是拷貝以後的x值。而自己的x值是不變的。
第一次看到這個話題,你們可能會很驚訝,substring方法竟然會致使內存泄露?這個話題要從JDK 6開始講起。
咱們先看下JDK 6的實現:
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; } public String substring(int beginIndex, int endIndex) { //check boundary return new String(offset + beginIndex, endIndex - beginIndex, value); }
能夠看到,JDK 6的substring方法底層仍是引用的原始的字符串數組。惟一的區別就是offset和count不一樣。
咱們考慮一下下面的應用:
String string = "abcdef"; String subString = string.substring(1, 3); string = null;
雖然最後咱們將String賦值爲null,可是subString仍然引用了最初的string。將不會被垃圾回收。
在JDK 7以後,String的實現發送了變化:
public String(char value[], int offset, int count) { //check boundary this.value = Arrays.copyOfRange(value, offset, offset + count); } public String substring(int beginIndex, int endIndex) { //check boundary int subLen = endIndex - beginIndex; return new String(value, beginIndex, subLen); }
Arrays.copyOfRange將會拷貝一份新的數組,而不是使用以前的數組。從而不會發生上面的內存泄露的問題。
雖然String是咱們常常使用的對象,可是裏面的原理仍是值得咱們瞭解的。
本文做者:flydean程序那些事本文連接:http://www.flydean.com/string-all-in-one/
本文來源:flydean的博客
歡迎關注個人公衆號:程序那些事,更多精彩等着您!