一文弄懂String的全部小祕密

簡介

String是java中很是經常使用的一個對象類型。能夠說java中使用最多的就是String了。那麼String到底有哪些祕密呢?接下來本文將會一一講解。java

String是不可變的

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() 致使的內存泄露

第一次看到這個話題,你們可能會很驚訝,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的博客

歡迎關注個人公衆號:程序那些事,更多精彩等着您!

相關文章
相關標籤/搜索