ByteBuffer源碼分析

引言

在nio中,流的讀取和寫入都是依賴buffer的。jdk在nio包中提供了ByteBuffer、CharBuffer、ShortBuffer、LongBuffer、DoubleBuffer、FloatBuffer等。 6中類型的buffer還分爲兩種實現,緩存在jvm堆中和緩存在直接內存中。

Buffer

主要屬性

// Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;

    // Used only by direct buffers
    // NOTE: hoisted here for speed in JNI GetDirectBufferAddress
    long address;

主要方法

這些方法是用來控制buffer的讀寫。
jdk提供的buffer只有一個position指針,讀和寫都是從position的位置開始操作。
代碼的流程常常是這樣的:
buffer.put(1);
buffer.flip();
buffer.get();
用於多次讀取操作

public final Buffer mark() {
        mark = position;
        return this;
    }

public final Buffer reset() {
        int m = mark;
        if (m < 0)
            throw new InvalidMarkException();
        position = m;
        return this;
    }

假裝清空

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
    }

翻轉指針

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

剩餘容量

public final int remaining() {
        return limit - position;
    }

獲取讀寫指針的方法

final int nextGetIndex() {                          // package-private
        if (position >= limit)
            throw new BufferUnderflowException();
        return position++;
    }

final int nextPutIndex() {                          // package-private
        if (position >= limit)
            throw new BufferOverflowException();
        return position++;
    }

ByteBuffer

主要屬性

// byte數組
final byte[] hb;                  // Non-null only for heap buffers
// offset 在派生的時候有用
    final int offset;

實例化方法

創建指定容量的heapBuffer和directBuffer

public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }

public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

通過數組創建ByteBuffer

public static ByteBuffer wrap(byte[] array,
                                    int offset, int length)
    {
        try {
            return new HeapByteBuffer(array, offset, length);
        } catch (IllegalArgumentException x) {
            throw new IndexOutOfBoundsException();
        }
    }

put和get方法

public ByteBuffer put(byte x) {
		
		// 獲取put的指針 
        hb[ix(nextPutIndex())] = x;
        return this;
    }

public byte get() {
		// 獲取get的指針
        return hb[ix(nextGetIndex())];
    }

protected int ix(int i) {
        return i + offset;
    }

派生ByteBuffer
slice創建的Buffer,讀寫都是在數組的子序列上進行。依賴於Buffer的當前索引

// 共享數組,position=0 mark=-1 limit=cap,
public ByteBuffer slice() {
        return new HeapByteBuffer(hb,
                                        -1,
                                        0,
                                        this.remaining(),
                                        this.remaining(),
                                        this.position() + offset);
    }

複製一個對象,共享數組,擁有相同數值的mark、position、limit、capacity和offset

public ByteBuffer duplicate() {
        return new HeapByteBuffer(hb,
                                        this.markValue(),
                                        this.position(),
                                        this.limit(),
                                        this.capacity(),
                                        offset);
    }

把已經在本buffer寫的元素移動到0位置,position定位到剩餘容量起點,limit限制爲capacity

public ByteBuffer compact() {
        System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
        position(remaining());
        limit(capacity());
        discardMark();
        return this;
    }

常用代碼段

// 拷貝文件
public void copyFile(String copyFrom,String copyTo){
	File file = new File(copyFrom);
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
        FileChannel channel = randomAccessFile.getChannel();

        File outFile = new File(copyTo);
        RandomAccessFile outRAF = new RandomAccessFile(outFile, "rw");
        FileChannel outChannel = outRAF.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (channel.read(byteBuffer) > 0){
            while (byteBuffer.hasRemaining()){
                byteBuffer.flip();
                outChannel.write(byteBuffer);
            }
        }
}

// 從網絡中讀取文件
public void readNetworkFile(){
		File outFile = new File("d://20200418220925641.png");
        RandomAccessFile outRAF = new RandomAccessFile(outFile, "rw");
        FileChannel outChannel = outRAF.getChannel();
        // get stream from net
        InputStream inputStream = new URL("https://img-blog.csdnimg.cn/20200418220925641.png").openConnection()
            .getInputStream();
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
        byte[] cache = new byte[2 * 1024];
        ByteBuffer wrap = ByteBuffer.wrap(cache);
        int len;
        while ((len = bufferedInputStream.read(cache)) > 0){
            wrap.position(0);
            wrap.limit(len);
            outChannel.write(wrap);
        }
}

public void writeMsgToClient(){
		ServerSocket serverSocket = new ServerSocket(9988);
		// bad case
        Socket accept = serverSocket.accept();
        SocketChannel channel = accept.getChannel();
        // do business logic and get a byte array
        byte[] rlt = "businessLogicRlt".getBytes();
        ByteBuffer wrap = ByteBuffer.wrap(rlt, 0, rlt.length);
        channel.write(wrap);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章