Home  >  Article  >  Java  >  Detailed explanation of code and text of Java NIO buffer

Detailed explanation of code and text of Java NIO buffer

黄舟
黄舟Original
2017-03-31 11:09:121640browse

A Buffer Object is a container for a fixed amount of data. A channel is the entry through which I/O transfers occur, and a buffer is the source or destination of these data transfers.

Buffer Basics

All buffers have four attributes to provide information about the data elements they contain.

  • capacity (capacity): The maximum value of data that the buffer can hold. It cannot be changed after the buffer is created.

  • limit (upper bound): The first element of the buffer that cannot be read or written. Alternatively, the count of existing elements in the buffer.

  • position(position): index of the next element to be read or written. Calling the get or put function will update .

  • mark (mark): A memo location. Call mark() to set mark=postion. Call reset() to set position= mark. The tag is undefined until it is set.

There is always the following relationship between these four properties:

0 <= mark <= position <= limit <= capacity

The picture below is a newly created ByteBuffer:

The position is set to 0, and the capacity and upper bound are set to 10, so as to pass the last byte that the buffer can hold. The tag is initially undefined. The capacity is fixed, but the other three properties can be changed when using the buffer.

The following is the method signature of Buffer:

public abstract class Buffer {

    public final int capacity() {
    }

    public final int position() {
    }

    public final Buffer position(int newPosition) {
    }

    public final int limit() {
    }

    public final Buffer limit(int newLimit) {
    }

    public final Buffer mark() {
    }

    public final Buffer reset() {
    }

    public final Buffer clear() {
    }

    public final Buffer flip() {
    }

    public final Buffer rewind() {
    }

    public final int remaining() {
    }

    public final boolean hasRemaining() {
    }

    public abstract boolean isReadOnly();

    public abstract boolean hasArray();

    public abstract Object array();

    public abstract int arrayOffset();

    public abstract boolean isDirect();
}

The Buffer API listed above does not include get() or put() functions. Each Buffer class has these two functions, but the parameter types they take, and the data type they return, are unique to each subclass, so they cannot be used in the top-level Buffer class is declared abstractly.

The following is the declaration of ByteBuffer:

public abstract class ByteBuffer extends Buffer implements Comparable
{
    // This is a partial API listing
    public abstract byte get( );
    public abstract byte get (int index);
    public abstract ByteBuffer put (byte b);
    public abstract ByteBuffer put (int index, byte b);
}

An example looks at the storage of ByteBuffer:

buffer.put((byte)&#39;H&#39;).put((byte)&#39;e&#39;).put((byte)&#39;l&#39;).put((byte)&#39;l&#39;).put((byte)&#39;o&#39;);

If you are performing the following operations:

buffer.put(0,(byte)&#39;M&#39;).put((byte)&#39;w&#39;);

When the buffer is full and to read out the content, we need to flip the buffer and call the flip method. The following is the flip method in ByteBuffer:

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

The rewind() function is similar to flip(), but does not affect the upper bound property. It just sets the position value back to 0. You can use rewind() to reread the data in the buffer that has been flipped.

The hasRemaining() method will return whether the upper bound of the buffer is currently reached.

The remaining() method will return the distance to the upper bound.

The mark of the buffer is undefined before the mark() function is called. When called, the mark is set to the value of the current position.

reset() function sets the position to the current tag value. If the mark value is undefined, calling reset() will result in an InvalidMarkException exception.

rewind( ), clear( ), and flip( ) always discard the flag, which is set to -1.

The necessary and sufficient conditions for two buffers to be considered equal are:

  • The two objects are of the same type. Buffers containing different data types are far from equal, and buffers are never equal to non-buffer objects.

  • Both objects have the same number of elements remaining. The capacities of the Buffers do not need to be the same, nor do the indices of the remaining data in the buffers. But the number of remaining elements (from position to upper bound) in each buffer must be the same.

  • The sequence of remaining data elements that should be returned by the Get() function must be consistent in each buffer.

Create buffer

//分配一个容量为 100 个 char 变量的 Charbuffer
CharBuffer charBuffer = CharBuffer.allocate (100);

char [] myArray = new char [100];
CharBuffer charbuffer = CharBuffer.wrap (myArray);
//这段代码构造了一个新的缓冲区对象,但数据元素会存在于数组中。
这意味着通过调用 put()函数造成的对缓冲区的改动会直接影响这个数组,而且对这个数组的任何改动也会对这 个缓冲区对象可见。

CharBuffer.wrap(array, offset, length);可以指定 position 和 length

Create through allocate() or wrap() function The buffers are usually indirect, and indirect buffers use backup arrays. hasArray() Returns whether this buffer has an accessible backing array.

Copy buffer

Some copy buffer APIs:

public abstract class CharBuffer extends Buffer implements CharSequence, Comparable {
	// This is a partial API listing
	public abstract CharBuffer duplicate( );
	public abstract CharBuffer asReadOnlyBuffer( );
	public abstract CharBuffer slice( );
}

duplicate() 函数创建了一个与原始缓冲区相似的新缓冲区。两个缓冲区共享数据元素,拥有同样的容量,但每个缓冲区拥有各自的位置,上界和标记属性。对一个缓冲区内的数据元素所做的改变会反映在另外一个缓冲区上。如果原始的缓冲区为只读,或者为直接缓冲区,新的缓冲区将继承这些属性。

如下示例:

public static void main(String[] args) {
		CharBuffer buffer = CharBuffer.allocate(8);
		buffer.position(3).limit(6).mark().position(5);
		CharBuffer dupeBuffer = buffer.duplicate();
		System.out.println(dupeBuffer.position());
		System.out.println(dupeBuffer.limit());
		dupeBuffer.clear();
		System.out.println(dupeBuffer.position());
		System.out.println(dupeBuffer.limit());
	}

//out
5
6
0
8

asReadOnlyBuffer() 函数来生成一个只读的缓冲区图。

代码说明如下:

public static void main(String[] args) {
		CharBuffer buffer = CharBuffer.allocate(8);
		CharBuffer dupeBuffer = buffer.asReadOnlyBuffer();
		System.out.println(dupeBuffer.isReadOnly());
		dupeBuffer.put(&#39;S&#39;);//只读buffer调用抛出异常
	}

//out
true
Exception in thread "main" java.nio.ReadOnlyBufferException
	at java.nio.HeapCharBufferR.put(HeapCharBufferR.java:172)
	at nio.test.TestMain.main(TestMain.java:10)

slice() 创建一个从原始缓冲区的当前位置开始的新缓冲 区,并且其容量是原始缓冲区的剩余元素数量(limit-position)。这个新缓冲区与原始 缓冲区共享一段数据元素子序列。分出来的缓冲区也会继承只读和直接属性。

public static void main(String[] args) {
		CharBuffer buffer = CharBuffer.allocate(8);
		buffer.position(3).limit(5);
		CharBuffer sliceBuffer = buffer.slice();
	}

字节缓冲区

在 java.nio 中,字节顺序由 ByteOrder 类封装。

ByteOrder.nativeOrder() 方法返回 JVM 运行的硬件平台字节顺序。

直接缓冲区

只有字节缓冲区有资格参与 I/O 操作。

I/O 操作的目标内存区域必须是连续的字节序列。

直接缓冲区被用于与通道和固有 I/O 例程交互。

直接字节缓冲区通常是 I/O 操作最好的选择。直接字节缓冲区支持 JVM 可用的最高效 I/O 机制。非直接字节缓冲区可以被传递给通道,但是这样可能导致性能耗。通常非直接缓冲不可能成为一个本地 I/O 操作的目标。如果向一个通道中传递一个非直接 ByteBuffer 对象用于写入会每次隐含调用下面的操作:

  1. 创建一个临时的直接 ByteBuffer 对象。

  2. 将非直接缓冲区的内容复制到直接临时缓冲中。

  3. 使用直接临时缓冲区执行低层次 I/O 操作。

  4. 直接临时缓冲区对象离开作用域,并最终成为被回的无用数据。

直接缓冲区时 I/O 的最佳选择,但可能比创建非直接缓冲区要花费更高的成本。直接缓冲区使用的内存是通过调用本地操作系统方面的代码分配的, 过了标准 JVM 。

ByteBuffer.allocateDirect() 创建直接缓冲区。isDirect() 返回是否直接缓冲区。

视图缓冲区

视图缓冲区通过已存在的缓冲区对象实例的工方法来创建。这种图对象维护它自己的属性,容量,位置,上界和标记,但是和原来的缓冲区共享数据元素。

ByteBuffer 类允许创建图来将 byte 型缓冲区字节数据映射为其它的原始数据类型。

public abstract CharBuffer asCharBuffer( );
public abstract ShortBuffer asShortBuffer( );
public abstract IntBuffer asIntBuffer( );
public abstract LongBuffer asLongBuffer( );
public abstract FloatBuffer asFloatBuffer( );
public abstract DoubleBuffer asDoubleBuffer( );

看如下一个例子的示意图:

ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer( );

public class BufferCharView {
       public static void main (String [] argv)  throws Exception  {
          ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN);
          CharBuffer charBuffer = byteBuffer.asCharBuffer( );
          // Load the ByteBuffer with some bytes
          byteBuffer.put (0, (byte)0);
          byteBuffer.put (1, (byte)&#39;H&#39;);
          byteBuffer.put (2, (byte)0);
          byteBuffer.put (3, (byte)&#39;i&#39;);
          byteBuffer.put (4, (byte)0);
          byteBuffer.put (5, (byte)&#39;!&#39;);
          byteBuffer.put (6, (byte)0);
          println (byteBuffer);
          println (charBuffer);
       }
       // Print info about a buffer
       private static void println (Buffer buffer)  {
		  System.out.println ("pos=" + buffer.position() + ", limit=" +
          	buffer.limit() + ", capacity=" + buffer.capacity() + ": &#39;" + buffer.toString( ) + "&#39;");
	}
}

//运行 BufferCharView 程序的输出是:
//pos=0, limit=7, capacity=7: &#39;java.nio.HeapByteBuffer[pos=0 lim=7 cap=7]&#39;
//pos=0, limit=3, capacity=3: &#39;Hi!

数据元素视图

ByteBuffer 类为每一种原始数据类型 供了存取的和转化的方法:

public abstract class ByteBuffer extends Buffer implements Comparable {
	public abstract char getChar();

	public abstract char getChar(int index);

	public abstract short getShort();

	public abstract short getShort(int index);

	public abstract int getInt();

	public abstract int getInt(int index);

	public abstract long getLong();

	public abstract long getLong(int index);

	public abstract float getFloat();

	public abstract float getFloat(int index);

	public abstract double getDouble();

	public abstract double getDouble(int index);

	public abstract ByteBuffer putChar(char value);

	public abstract ByteBuffer putChar(int index, char value);

	public abstract ByteBuffer putShort(short value);

	public abstract ByteBuffer putShort(int index, short value);

	public abstract ByteBuffer putInt(int value);

	public abstract ByteBuffer putInt(int index, int value);

	public abstract ByteBuffer putLong(long value);

	public abstract ByteBuffer putLong(int index, long value);

	public abstract ByteBuffer putFloat(float value);

	public abstract ByteBuffer putFloat(int index, float value);

	public abstract ByteBuffer putDouble(double value);

	public abstract ByteBuffer putDouble(int index, double value);
}

假如一个 bytebuffer 处于如下状态:

那么 int value = buffer.getInt();

实际的返回值取决于缓冲区的当前的比特排序(byte-order)设置。更具体的写法是:

int value = buffer.order (ByteOrder.BIG_ENDIAN).getInt( );

这将会返回值 0x3BC5315E,同时:

int value = buffer.order (ByteOrder.LITTLE_ENDIAN).getInt( );

返回值 0x5E31C53B

如果您试图获取的原始类型需要比缓冲区中存在的字节数更多的字节,会抛出 BufferUnderflowException。

The above is the detailed content of Detailed explanation of code and text of Java NIO buffer. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn