缓冲区Buffer

文章目录

  • 缓冲区介绍
  • Buffer类的使用
    • 包装数据与获得容量
    • 限制获取与设置
    • 位置获取与设置
    • 剩余空间大小获取

缓冲区介绍

abstract class Buffer
7个直接子类
abstract class ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。
NIO中的Buffer是一个用于存储脚本数据类型的容器,以类似于数组有序的方式来存储和组织数据。

Buffer类的使用

API:
缓冲区Buffer_第1张图片

包装数据与获得容量

	//Invariants: mark <= position <= limit <= capacity
	private int mark = -1; //标记
    private int position = 0; //位置
    private int limit; //限制
    private int capacity; //容量

Buffer的7个子类也是抽象的,不能通过new 实例化,使用静态方法wrap()实现。wrap方法将数组放入缓冲区,来构建存储不同数据类型的缓冲区。

	byte[] byteArray = new byte[]{1,2,3};
    ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
    System.out.println(byteBuffer.getClass().getName());
    System.out.println(byteBuffer.capacity());

output:
	java.nio.HeapByteBuffer
	3

wrap源码

  /**
     * Wraps a byte array into a buffer.
     *
     * 

The new buffer will be backed by the given byte array; * that is, modifications to the buffer will cause the array to be modified * and vice versa. The new buffer's capacity and limit will be * array.length, its position will be zero, and its mark will be * undefined. Its {@link #array backing array} will be the * given array, and its {@link #arrayOffset array offset>} will * be zero.

…… */ public static ByteBuffer wrap(byte[] array) { return wrap(array, 0, array.length); } /**…… * * @param array * The array that will back the new buffer * @param offset * The offset of the subarray to be used; must be non-negative and * no larger than array.length. The new buffer's position * will be set to this value. * @param length * The length of the subarray to be used; * must be non-negative and no larger than * array.length - offset. * The new buffer's limit will be set to offset + length. * @return The new byte buffer */ public static ByteBuffer wrap(byte[] array,int offset, int length){ try { return new HeapByteBuffer(array, offset, length); } catch (IllegalArgumentException x) { throw new IndexOutOfBoundsException(); } }

ByteBuffer类缓冲区的原理是使用byte[]数组进行数据的保存,在后续使用指定的API来操作这个数组已达到操作缓冲区的目的。

	HeapByteBuffer(byte[] buf, int off, int len) { // package-private
        super(-1, off, off + len, buf.length, buf, 0);
    }

	ByteBuffer(int mark, int pos, int lim, int cap, byte[] hb, int offset){
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }

从源码可以看到,缓冲区存储的数据还是存储在byte[]字节数组中。使用缓冲区与使用byte[]字节数组的优点在于缓冲区将存储数据的byte[]字节数组内容与相关的信息整合在一个Buffer类中,将数据与缓冲区中的信息进行了整合并封装,便于获得相关的信息及处理数据。

限制获取与设置

限制代表第一个不应该读取或写入元素的index

limit源码:

	/**
     * Returns this buffer's limit.
     */
    public final int limit() {
        return limit;
    }
	/**
     * Sets this buffer's limit.  If the position is larger than the new limit
     * then it is set to the new limit.  If the mark is defined and larger than
     * the new limit then it is discarded.
     *
     * @param  newLimit
     *         The new limit value; must be non-negative
     *         and no larger than this buffer's capacity
     *
     * @return  This buffer
     *
     * @throws  IllegalArgumentException
     *          If the preconditions on newLimit do not hold
     */
    public final Buffer limit(int newLimit) {
        if ((newLimit > capacity) || (newLimit < 0))
            throw new IllegalArgumentException();
        limit = newLimit;
        if (position > limit) position = limit;
        if (mark > limit) mark = -1;
        return this;
    }

示例

		char[] charArr = new char[]{'a','b','c','d','e','f'};
        CharBuffer charBuffer = CharBuffer.wrap(charArr);
        System.out.println("capacity = " + charBuffer.capacity() + " limit=" + charBuffer.limit());
        
        charBuffer.limit(3);
        System.out.println("capacity = " + charBuffer.capacity() + " limit=" + charBuffer.limit());
        
        charBuffer.put(0,'h');
        charBuffer.put(1,'i');
        charBuffer.put(2,'j');
        charBuffer.put(3,'k');  //第一个不可读不可写的索引
        charBuffer.put(4,'l');
output:
	capacity = 6 limit=6
	capacity = 6 limit=3
	java.lang.IndexOutOfBoundsException
		……

Limit的使用场景是当反复向缓冲区中存储数据时使用,如第一次向缓冲区写入 A到G 7个字符,然后全部读取,第二次向缓冲区写入1到4 4个字符,如果读取到1、2、3、4、E、F、G是错误的,需要结合limit限制读取范围。

位置获取与设置

源码

/**
     * Returns this buffer's position.
     *
     * @return  The position of this buffer
     */
    public final int position() {
        return position;
    }

    /**
     * Sets this buffer's position.  If the mark is defined and larger than the
     * new position then it is discarded.
     *
     * @param  newPosition
     *         The new position value; must be non-negative
     *         and no larger than the current limit
     *
     * @return  This buffer
     *
     * @throws  IllegalArgumentException
     *          If the preconditions on newPosition do not hold
     */
    public final Buffer position(int newPosition) {
        if ((newPosition > limit) || (newPosition < 0))
            throw new IllegalArgumentException();
        position = newPosition;
        if (mark > position) mark = -1;
        return this;
    }

剩余空间大小获取

返回当前位置与limit之间的元素数

/**
     * Returns the number of elements between the current position and the
     * limit.
     *
     * @return  The number of elements remaining in this buffer
     */
    public final int remaining() {
        return limit - position;
    }
    ```

## 使用Buffer mark()方法处理标记
缓冲区的 标记是一个索引,在调用reset()方法时,会将缓冲区的position位置重置为该索引。标记mark并不是必须的。定义mark时,不能将其定义为负数,并且不能让他大于position。如果定义了mark,则在将position或limit调整为小于该mark的值时,该mark将被丢弃,丢弃后的mark值是-1.如果未定义mark,那么调用reset方法将导致抛出invalidMarkException。

你可能感兴趣的:(#,NIO与Socket编程)