Notes 20180309 : String第一講_char的可讀序列

  實際上在寫本文以前,我曾考慮是先探討面向對象,仍是先選擇String和Arrays,最後仍是選擇了後者,並不是是面向對象對咱們不重要,相反它是Java的靈魂所在,之因此這樣的安排是由於這兩個是在是咱們程序中最爲常見的了,因此放在面向對象前面講解,開始講解String以前,我先說一下我對於類的一些探討路線,咱們依據面向接口編程來探討,首先將類似類的共同方法所在接口進行談論,而後纔是類,本文亦是如此,咱們先來說解char的可讀序列_CharSequence,下面開始今天的學習。算法

1.可讀序列

  咱們查看String,發現其實現了CharSequence接口,而在StringBuffer和StringBuilder中也有該接口的實現,這就不得不引發個人注意了,經過API查閱發現對於CharSequence的解釋以下【編程

public interface CharSequence數組

  CharSequence 是 char 值的一個可讀序列(也即Unicode字符序列,如"Java\u6211",表示的就是五個字符 'J','a','v''a','我')。此接口對許多不一樣種類的 char 序列(這意味着只要是char序列,那麼多半就要實現該接口了,從這裏咱們也就明白了爲何String、StringBuilder、StringBuffer、CharBuffer爲何都實現了該接口,由於這三個底層都是字符數組,即標準的字符序列)提供統一的只讀訪問。char 值表示 Basic Multilingual Plane (BMP 基本多語言平面) 或代理項中的一個字符。有關詳細信息,請參閱 Unicode 字符表示形式,說白了就是char----Unicode----數值。 此接口不修改 equals 和 hashCode 方法的常規協定。所以,一般未定義比較實現 CharSequence 的兩個對象的結果。每一個對象均可以經過一個不一樣的類實現,並且不能保證每一個類可以測試其實例與其餘類的實例的相等性。所以,使用任意 CharSequence 實例做爲集合中的元素或映射中的鍵是不合適的。下圖是截取的全部實現CharSequence接口的類,咱們在後面一一認識。】數據結構

2.接口的方法摘要

  返回值       方法函數

  char          charAt(int index)返回指定索引的char值【該接口是char的字符序列,既然是序列,那麼就必定是有長度的,咱們天然能夠經過索引獲取相應位置的字符,因此將該方法提取到接口中是頗有必要的,這也是將具備相同屬性和行爲抽取到一個類中的鮮明實現!】
學習

  int           length()返回次字符序列的長度
測試

    CharSequence  subSequence(int start,intend)返回一個新的CharSequence,它是此序列的子序列。【最初在研究String的時候,並無研究CharSequence,當時對於該方法很不理解,知道後來研究了該接口,才以爲很聰明的選擇,它沒用用一個特定的對象來做爲返回值,而是一個接口,這樣就能夠動態返回返回值,經過多態將該字符序列的實現類對象向上轉型爲接口】ui

  String        toString()返回一個包含此序列中字符的字符串,該字符串與此序列的順序相同。【這是重寫Object的方法,接口並無顯式繼承Object類,實際上也不必,Object默認是全部類和接口的父類,重寫toString()返回以字符串形式表示的字符序列,而不是Object中的hashcode】
this

3.小提醒

  在"可讀序列"中有這麼一句話,讀時有所疑惑,因此在這裏拿出來再作說明,"此接口不修改 equals 和 hashCode 方法的常規協定。所以,一般未定義比較實現 CharSequence 的兩個對象的結果。每一個對象均可以經過一個不一樣的類實現,並且不能保證每一個類可以測試其實例與其餘類的實例的相等性。所以,使用任意 CharSequence 實例做爲集合中的元素或映射中的鍵是不合適的"。咱們在字符序列中使用equals()每每只但願比對字符序列內容,而非是對象引用地址(經過hashcode),可是CharSequence並無重寫equals(),因此若是咱們經過多態將CharSequence的實現類對象向上轉型爲接口,那麼此時再使用equals(),若是是同一個類的對象那麼比對是經過該類重寫的equals(),若不是同一個對象,那麼是經過Object的equals方法來比對(經過hashcode),而不是字符序列內容,即使兩個實現類重寫了equals(),舉例以下:spa

package cn.charsequence;

public class CharSequenceTest {
    public static void main(String[] args) {
        String str1 = new String("AAA");
        String str2 = "AAA";
        System.out.println(str1.equals(str2));
        StringBuffer sb = new StringBuffer("AAA");
        CharSequence cs1 = sb;
        System.out.println(cs1.equals(str2));
    }
}

  若是以對象做爲集合的元素,也會出現這種狀況,由於集合中的元素或鍵是經過equals()來比對的,可是若是是散列表中的鍵,那麼是經過hashcode來比對的,由於散列表中鍵是不能重複的,以下:

//        list.add("AAA");
        list.add(new String("AAA"));
        list.add(new StringBuffer("AAA"));
        Map<CharSequence, String> map = new HashMap<>();
        map.put("AAA", "String_AAA");
        map.put(new StringBuffer("AAA"), "StringBuffer_AAA");
        System.out.println(list.contains("AAA"));
        System.out.println(map.get("AAA"));
        System.out.println(map.get(new StringBuffer("AAA")));

4.字符序列的底層是字符數組

  咱們講字符序列,那麼究竟什麼是字符序列呢?其實字符序列就是由一個以上的字符構成的序列,這裏的序列講的是一種數據結構,換用到這裏說的就是數組了,咱們經過一段代碼來看一下:

private final char value[];   
  public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

  這是字符串String中的一個構造方法,在這個構造函數中使用到了數組的copyOf()方法,該方法的具體做用以下:

 

  那麼這個構造函數其實就是以咱們傳遞進來的字符數組爲範本而後建立一個新的字符數組,內容同樣,而後賦值給String中已經聲明可是未經初始化的字符數組,因此咱們日常看到的字符串,其底層其實是一個字符數組,那麼咱們可能會有這麼一個疑問,那就是爲何輸出的時候輸出字符串,在控制檯上仍是以字符串出現呢,數組(對象)的輸出不是一般會輸出一個16進制的hashCode嗎,這和咱們所說的不是矛盾嗎?下面咱們以一段代碼來分析:

    /**
     * String的底層是char數組
     */
    @Test
    public void fun3(){
        String[] str = {"花褪殘紅青杏小。","燕子飛時,","綠水人家繞。","枝上柳綿吹又少,","天涯何處無芳草。"};
        int[] i = {1,2,3,4,5};
        boolean[] boo = {true,false};
        char[] ch = {'花','褪','殘','紅','青','杏','小'};
        System.out.print(str);
        System.out.println("     "+str+"-----"+Arrays.toString(str));
        System.out.print(i);
        System.out.println("     "+i+"-----"+Arrays.toString(i));
        System.out.print(boo);
        System.out.println("     "+boo+"-----"+Arrays.toString(boo));
        System.out.print(ch);
        System.out.println("     "+ch+"-----"+Arrays.toString(ch));
    }

  查看結果咱們發現,當咱們數組對象時,大部分數組都會輸出一個看起來像是地址的字符串,只有char數組,輸出了一個字符串,字符串內容是字符數組的元素。這是爲何呢?然咱們一步步經過代碼來解析:

首選看輸出語句打開看它的底層代碼以下:printStraem.class

    public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

觀察上面的代碼,咱們知道輸出會將一個object經過String.valueOf()方法轉變爲String,觀察該方法

public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }
public static String valueOf(char data[]) {
        return new String(data);
    }
public static String valueOf(char data[], int offset, int count) {
        return new String(data, offset, count);
    }

  在String類中涉及到的valueOf方法有這三個,觀察發現,其中兩個都是關於char數組的,而一個是涉及的其它對象的,那麼數組的輸出在這裏劃出了兩條路線,觀察字符發現,它會把字符數組做爲參數構建一個字符串對象.觀察那個參數是Object的,若是不是null,那麼會返回一個字符串,內容是Object的toString方法

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

那麼此時到了這裏,字符數組變成了一個字符串對象,底層仍然是字符數組,而其餘類型則變成了一個字符串,內容是16進制的HashCode,接下來咱們看對字符串的打印輸出

    public void print(String s) {
        if (s == null) {
            s = "null";
        }
        write(s);
    }
 private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

  到了這裏咱們大概就明白了,使用字符流輸出到控制檯字符數組變成了字符串,而其餘對象輸出的確實一個看起來像是地址的HashCode(HashCode和地址不同,它有時候並不是是真實的物理地址,只是一種算法罷了,詳細的hashcode咱們在介紹Object的時候在介紹).這裏咱們經過String來介紹了一下,實際上CharSequence大體原理是一致的.

相關文章
相關標籤/搜索