Java基礎-關鍵字-String

來源:互聯網
上載者:User

標籤:

 

1、String的本質

安全執行緒  

  開啟String的源碼,類注釋中有這麼一段話“Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings.Because String objects are immutable they can be shared.”。這句話總結歸納了String的一個最重要的特點:String是值不可變(immutable)的常量,是安全執行緒的(can be shared)。

 

不可繼承

  String類使用了final修飾符,表明了String類的第二個特點:String類是不可繼承的。

  在Java中,被final修飾的類是不允許被繼承的,並且該類中的成員方法都預設為final方法。在早期的JVM實現版本中,被final修飾的方法會被轉為內嵌調用以提升執行效率。而從Java SE5/6開始,就漸漸擯棄這種方式了。因此在現在的Java SE版本中,不需要考慮用final去提升方法調用效率。只有在確定不想讓該方法被覆蓋時,才將方法設定為final。

 

值不可變,儲存方式為字元數組

private final char value[];private final int count; 

 

  從final的修飾可以看出其不可變性

  其中的String類的 concat方法為起擴容,如果方法的參數長度等於0這返回this,否則並組成一個新的string,並緊隨其後拼接參數後返回新的string

  我們看String類的concat方法。實現該方法第一步要做的肯定是擴大成員變數value的容量,擴容的方法重新定義一個大容量的字元數組buf。第二步就是把原來value中的字元copy到buf中來,再把需要concat的字串值也copy到buf中來,這樣子,buf中就包含了concat之後的字串值。下面就是問題的關鍵了,如果value不是final的,直接讓value指向buf,然後返回this,則大功告成,沒有必要返回一個新的String對象。但是。。。可惜。。。由於value是final型的,所以無法指向新定義的大容量數組buf,那怎麼辦呢?“return new String(0, count + otherLen, buf);”,這是String類concat實現方法的最後一條語句,重新new一個String對象返回。這下真相大白了吧!

  String類其實是通過char數組來儲存字串的。

 

總結:String實質是字元數組,兩個特點:1、該類不可被繼承;2、不可變性(immutable)

 

public String substring(int beginIndex, int endIndex) {    if (beginIndex < 0) {        throw new StringIndexOutOfBoundsException(beginIndex);    }    if (endIndex > count) {        throw new StringIndexOutOfBoundsException(endIndex);    }    if (beginIndex > endIndex) {        throw new StringIndexOutOfBoundsException(endIndex - beginIndex);    }    return ((beginIndex == 0) && (endIndex == count)) ? this :        new String(offset + beginIndex, endIndex - beginIndex, value);    }  public String concat(String str) {    int otherLen = str.length();    if (otherLen == 0) {        return this;    }    char buf[] = new char[count + otherLen];    getChars(0, count, buf, 0);    str.getChars(0, otherLen, buf, count);    return new String(0, count + otherLen, buf);    }  public String replace(char oldChar, char newChar) {    if (oldChar != newChar) {        int len = count;        int i = -1;        char[] val = value; /* avoid getfield opcode */        int off = offset;   /* avoid getfield opcode */         while (++i < len) {        if (val[off + i] == oldChar) {            break;        }        }        if (i < len) {        char buf[] = new char[len];        for (int j = 0 ; j < i ; j++) {            buf[j] = val[off+j];        }        while (i < len) {            char c = val[off + i];            buf[i] = (c == oldChar) ? newChar : c;            i++;        }        return new String(0, len, buf);        }    }    return this;

從上面的三個方法可以看出,無論是sub操、concat還是replace操作都不是在原有的字串上進行的,而是重建了一個新的字串對象。也就是說進行這些操作後,最原始的字串並沒有被改變。

“對String對象的任何改變都不影響到原對象,相關的任何change操作都會產生新的對象”。

 

 

 2、String的記憶體機制

 

JVM運行時,會將記憶體分為兩個部分:堆和棧。堆中存放的是建立的對象,而棧中存放的方法調用過程的局部變數或引用。而設計Java字串對象記憶體實現的時候,在堆中又開闢了一塊很小的記憶體,稱之為字串常量池,專門用來存放特定的字串對象。

建立Java字串對象有兩種常用的方式:

String 引用變數名="字串內容";String 應用變數名=new String(<參數序列>);

我們先來看看建立字串對象的第一種方式記憶體如何分配的,代碼如下:

String s1="osEye.net";String s2="osEye.net";

 如描述了引用對象的關係,以及記憶體的分配。Java實現的步驟如下:

  1. 查看字串常量池中是否存在內容與“osEye.net”相同的字串對象。
  2. 若沒有,則新建立一個包含該內容的字串對象,並讓引用變數指向該對象。例如,建立字串s1的時候,字串常量池中沒有,則建立一個新對象,並讓引用s1指向該對象。
  3. 若已經存在包含該內容的字串對象,則讓字串引用直接指向該對象。例如,在建立字串s2的時候,字串常量池中已經有包含該內容的對象了,所以引用s2直接指向已有的對象。

在來看看第二種建立字串對象的方式:

String s1="osEye.net";String s2=new String("osEye.net");

如描述了引用對象的關係,以及記憶體的分配。Java實現的步驟如下:

  1. 首先在堆(不是常量池)中建立一個包含指定內容的字串對象,並將字串引用指向該對象。例如上述代碼中,使用new建立字串s3,其會直接在堆中建立一個內容為“osEye.net”的字串對對象,並將引用s3指向該對象。
  2. 去字串常量池中查看,是否有包含該內容的對象。
  3. 若有,則將new出來的字串對象與字串常量池中內容相同的對象聯絡起來。例如,本例中s3所指向的對象與s1所指向的聯絡起來。
  4. 若沒有,則在字串常量池再建立一個包含該內容的字串對象,並將堆中的對象與字串常量池中新建立出來的對象聯絡起來。

 

 我們知道,new出來的字串對象和字串常量池中的對象是有聯絡的,可以通過intern方法來查看,方法簽名:

public String intern()

此方法將指定的字串引用在字串常量池中對應的對象,若其指向的對象本身就在字串常量池中,則直接將自己指向的對象返回;若該字串引用指向的對象在堆中,則返回字串常量池中與其聯絡的對象。執行個體如下:

 

package net.oseye;public class ExceptionTest {     public static void main(String[] args) {        String s1="osEye.net";        String s2=new String("osEye.net");                if(s1==s2){            System.out.println("字串引用s1和字串引用s2所指向的是同一個對象");        }else{            System.out.println("字串引用s1和字串引用s2所指向的不是同一個對象");        }        if(s1.intern()==s2.intern()){            System.out.println("字串引用s1和字串引用s2在字串常量池中聯絡的是同一個對象");        }else{            System.out.println("字串引用s1和字串引用s2在字串常量池中聯絡的不是同一個對象");        }    }}

輸出結果:

字串引用s1和字串引用s2所指向的不是同一個對象字串引用s1和字串引用s2在字串常量池中聯絡的是同一個對象

 

Java基礎-關鍵字-String

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.