Buffer緩存區

Buffers閱讀筆記

一、簡介

這是一個緩存區的抽象類,真正的數組存儲在ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、CharBuffer、DoubleBuffer、FloatBuffer

二、繼承關係圖

在這裏插入圖片描述

  • 由上圖(可以放大)可以看出,Buffer是一個抽象類,有很多子類去實現,但是他們也都是抽象類,但是最終實現的是HeapxxxBuffer。Byte開頭的因爲特殊處理字節,所以會有一個Bits協助

三、存儲結構

  • 自身沒有存儲結構,由抽象子類xxxBuffer有一個存儲數組(同類型)

四、源碼分析

內部類

屬性

// Invariants: mark <= position <= limit <= capacity
private int mark = -1;// 標記,用於reset把position修改爲mark,如果mark=-1或未定義會拋異常
private int position = 0;// 當前位置,表示讀取或寫入的位置
private int limit;// 讀取/寫入的限制,當前索引開始及以後不可讀取和寫入
private int capacity;// 容量。代表該緩存中的容量

static final int SPLITERATOR_CHARACTERISTICS =
        Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;

long address;

構造

// 初始化構造器,同時初始化核心的4個屬性,注意此構造是採用的默認修飾,所以是不支持直接實例化
Buffer(int mark, int pos, int lim, int cap) {       // package-private
    if (cap < 0)
        throw new IllegalArgumentException("Negative capacity: " + cap);
    this.capacity = cap;
    limit(lim);//詳見主要方法
    position(pos);//詳見主要方法
    if (mark >= 0) {
        if (mark > pos)
            throw new IllegalArgumentException("mark > position: ("
                                               + mark + " > " + pos + ")");
        this.mark = mark;
    }
}

主要方法

1、讀取4大屬性
  • 讀取 4 大屬性

    public final int capacity() {return capacity;}
    public final int position() {return position;}
    public final int limit() {return limit;}
    // package-private,僅支持同包及本類訪問
    final int markValue() {return mark;}
    
2、單個操作4大屬性
  • 單個操作 4 大屬性

    /**
     * capacity 僅在實例化的時候設置 
     */
    
    /**
     * 設置position,涉及到邏輯處理
     */
    public final Buffer position(int newPosition) {
        // 邊界效驗
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        // 設置
        position = newPosition;
        // 1. mark 大於 position 直接設置爲-1,reset()就會拋出異常(mark小於0)
        if (mark > position) mark = -1;
        return this;
    }
    /**
     * 設置limit,涉及到邏輯處理
     */
    public final Buffer limit(int newLimit) {
        // 邊界效驗
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        // 設置
        limit = newLimit;
        // 1. position 大於limit,則直接設置position爲limit
        // 也代表讀寫操作都會拋出異常
        if (position > limit) position = limit;
        // 2. mark 大於 limit直接設置爲-1,reset()就會拋出異常(mark小於0)
        if (mark > limit) mark = -1;
        return this;
    }
    
    /**
     * 設置mark,在當前位置(position)設置標記
     */
    public final Buffer mark() {
        mark = position;
        return this;
    }
    
3、緩衝區4屬性處理
  • 緩衝區屬性處理

    /**
     * 還原緩衝區的數據
     * 還原一切狀態
     */
    public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }
    /**
     * 設置當前當前位置重置到標記位置
     */
    public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }
    /**
     * 反轉此緩衝區
     * 適合用於在緩衝區寫入數據,然後,再從緩衝區讀取數據(不用考慮limit限制)
     * 也就是使緩衝區爲“重新讀取”已包含的數據做好準備,他使限制保持不變,講位置設置爲0
     * 類似substring截取
     */
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }
    /**
     * 重繞緩衝區,將當前位置設置爲0,並丟棄標記
     * 適合用於重新讀取/寫入緩衝區時,(前提是設置了適當的limit限制)
     * 重新讀取重新吸入的時候可用
     */
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }
    
4、判斷剩餘量和底層數組
  • 判斷剩餘量和底層數組

    /**
     * 判斷是否還有剩餘可讀寫容量
     */
    public final boolean hasRemaining() {
        return position < limit;
    }
    /**
     * 抽象方法:是否是隻讀(非直接緩存(JVM中間緩存區)支持讀寫,但是直接緩存(內存)只支持讀)
     */
    public abstract boolean isReadOnly();
    /**
     * 抽象方法:JVM緩衝區是否有數組
     */
    public abstract boolean hasArray();
    /**
     * 抽象方法:是否是直接緩存區(直接跳過JVM中間緩存區)
     */
    public abstract boolean isDirect();
    
5、獲取剩餘容量和底層數組
  • 獲取剩餘容量和底層數組

    /**
     * 獲得剩餘可讀寫的容量
     */
    public final int remaining() {
        return limit - position;
    }
    /**
     * 抽象方法,獲取數組
     */
    public abstract Object array();
    /**
     * 獲取數組的偏移量
     */
    public abstract int arrayOffset();
    
6、其他package-private可用方法
  • 其他默認修飾方法

    /**
     * 所有子類get使用,使當前位置(position)+ 1,並返回原position索引
     */
    final int nextGetIndex() {                          // package-private
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }
    
    /**
     * 所有子類get使用,使當前位置(position)+ nb,並返回原position索引
     */
    final int nextGetIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferUnderflowException();
        int p = position;
        position += nb;
        return p;
    }
    
    /**
     * 同get一樣邏輯
     * 所有子類put使用,使當前位置(position)+ 1,並返回原position索引
     */
    final int nextPutIndex() {                          // package-private
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }
    
    /**
     * 同get一樣邏輯
     * 所有子類put使用,使當前位置(position)+ nb,並返回原position索引
     */
    final int nextPutIndex(int nb) {                    // package-private
        if (limit - position < nb)
            throw new BufferOverflowException();
        int p = position;
        position += nb;
        return p;
    }
    
    /**
     * 檢查 i 是否可讀寫
     */
    final int checkIndex(int i) {                       // package-private
        if ((i < 0) || (i >= limit))
            throw new IndexOutOfBoundsException();
        return i;
    }
    
    /**
     * 檢查 i 是否可讀寫,nb代表每次讀寫的位,一次讀寫的位超出limit則邊界異常
     */
    final int checkIndex(int i, int nb) {               // package-private
        if ((i < 0) || (nb > limit - i))
            throw new IndexOutOfBoundsException();
        return i;
    }
    
    /**
     * 讀取當前mark值
     */
    final int markValue() {                             // package-private
        return mark;
    }
    
    /**
     * 限制讀寫操作,直接所有設置最低值
     */
    final void truncate() {                             // package-private
        mark = -1;
        position = 0;
        limit = 0;
        capacity = 0;
    }
    
    /**
     * 丟棄原mark,直接設置爲-1
     */
    final void discardMark() {                          // package-private
        mark = -1;
    }
    
    /**
     * off 開始索引位
     * len 每次讀寫操作量
     * size 此數組容量長度
     * 效驗本次讀取是否超出邊界
     */
    static void checkBounds(int off, int len, int size) { // package-private
        if ((off | len | (off + len) | (size - (off + len))) < 0)
            throw new IndexOutOfBoundsException();
    }
    

補充

五、總結

抽象類 ShortBuffer、LongBuffer、IntBuffer、DoubleBuffer、FloatBuffer的所有函數基本上是一樣的,只是存儲的接口不同。所以操作有點卻別,但是函數一樣。

charBuffer由於需要操作字符,所以比上面的會多一些例如append,charAt、subDequence函數

ByteBuffer因爲也比上面多,例如_get、_put、putXXX、getXXX、asXXXBuffer等

heapXXXBuffer的實現類同上,也是相似和不同之處。原因請看後面的源碼分析

發佈了48 篇原創文章 · 獲贊 17 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章