Buffer(缓冲区)
在JDK NIO的模型中,所有的I/O都要缓冲,不再向输出流写入数据和从输入流读取数据,而是要从缓冲区中读写数据,像在缓冲流中一样,缓冲区可能就是字节数组。
Java的所有基本数据类型都有特定的Buffer子类:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。不过在网络编程中几乎只会使用ByteBuffer。
Buffer的结构
位置(position)
缓冲区中将读取或者写入的下一个位置,这个位置值从0开始计,最大值等于缓冲区的大小,可以用下面两个方法获取和设置:
/**
* 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 <tt>newPosition</tt> 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;
}
容量(capacaity)
缓冲区可以保存的元素的最大数目,容量值在创建缓冲区时设置,此后不能改变,可以用以下方法读取:
/**
* Returns this buffer's capacity.
*
* @return The capacity of this buffer
*/
public final int capacity() {
return capacity;
}
限度(limit)
缓冲区中可访问数据的末尾位置,只要不改变限度,就无法读/写超过这个位置的数据,即使缓冲区有更大的容量也没有用,限度可以用下面两个方法获取和设置:
/**
* Returns this buffer's limit.
*
* @return The limit of this buffer
*/
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 <tt>newLimit</tt> 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;
}
标记(mark)
缓冲区中客户端指定的索引,通过调用mark()可以将标记设置为当前位置,调用reset()可以将当前位置设置为所标记的位置。
/**
* Sets this buffer's mark at its position.
*
* @return This buffer
*/
public final Buffer mark() {
mark = position;
return this;
}
/**
* Resets this buffer's position to the previously-marked position.
*
* <p> Invoking this method neither changes nor discards the mark's
* value. </p>
*
* @return This buffer
*
* @throws InvalidMarkException
* If the mark has not been set
*/
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
如果将位置设置为低于现有的标记,则丢弃这个标记。
与读取InputStream不同,读取缓冲区实际上不会以任何方式改变缓冲区中的数据,只可能向前或者向后设置位置,
Buffer主要方法
clear
clear()方法将位置设置为0,并将限度设置为容量,从而将缓冲区“清空”。这样一来,就可以完全重新填充缓冲区了。
/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*
* <p> Invoke this method before using a sequence of channel-read or
* <i>put</i> operations to fill this buffer. For example:
*
* <blockquote><pre>
* buf.clear(); // Prepare buffer for reading
* in.read(buf); // Read data</pre></blockquote>
*
* <p> This method does not actually erase the data in the buffer, but it
* is named as if it did because it will most often be used in situations
* in which that might as well be the case. </p>
*
* @return This buffer
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
clear()方法没有删除缓冲区中的老数据,这些数据仍然存在。
flip
flip()方法将限度设置为当前位置,位置设置为0。
/**
* Flips this buffer. The limit is set to the current position and then
* the position is set to zero. If the mark is defined then it is
* discarded.
*
* <p> After a sequence of channel-read or <i>put</i> operations, invoke
* this method to prepare for a sequence of channel-write or relative
* <i>get</i> operations. For example:
*
* <blockquote><pre>
* buf.put(magic); // Prepend header
* in.read(buf); // Read data into rest of buffer
* buf.flip(); // Flip buffer
* out.write(buf); // Write header + data to channel</pre></blockquote>
*
* <p> This method is often used in conjunction with the {@link
* java.nio.ByteBuffer#compact compact} method when transferring data from
* one place to another. </p>
*
* @return This buffer
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
一般在操作完put方法之后,调用此方法来准备get,相反同样操作也可。
remaining
返回缓冲区中当前位置与限度之间的元素数。
/**
* 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;
}
hasRemaining
如果缓冲区中还有数据就返回true。
/**
* Tells whether there are any elements between the current position and
* the limit.
*
* @return <tt>true</tt> if, and only if, there is at least one element
* remaining in this buffer
*/
public final boolean hasRemaining() {
return position < limit;
}
基本使用演示
public class Test {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
System.out.println("position: " + buffer.position() + ", limit: " + buffer.limit() + ", capacity: " + buffer.capacity());
System.out.println();
buffer.put("123456".getBytes());
System.out.println("====== 调用put '123456' ======");
System.out.println("position: " + buffer.position() + ", limit: " + buffer.limit() + ", capacity: " + buffer.capacity());
System.out.println();
int remaining = buffer.remaining();
System.out.println("======= 调用remaining方法之后 ======");
System.out.println("remaining: " + remaining);
System.out.println();
buffer.flip();
System.out.println("======= 调用flip方法之后 ======");
System.out.println("position: " + buffer.position() + ", limit: " + buffer.limit() + ", capacity: " + buffer.capacity());
System.out.println();
remaining = buffer.remaining();
System.out.println("======= 调用remaining方法之后(注意观察flip之后,remaining的变化) ======");
System.out.println("remaining: " + remaining);
System.out.println();
buffer.get();
System.out.println("======= 调用get方法之后 ======");
System.out.println("position: " + buffer.position() + ", limit: " + buffer.limit() + ", capacity: " + buffer.capacity());
System.out.println();
remaining = buffer.remaining();
System.out.println("======= 调用remaining方法之后 ======");
System.out.println("remaining: " + remaining);
System.out.println();
buffer.clear();
System.out.println("======= 调用clear方法之后 ======");
System.out.println("position: " + buffer.position() + ", limit: " + buffer.limit() + ", capacity: " + buffer.capacity());
System.out.println();
remaining = buffer.remaining();
System.out.println("======= 调用remaining方法之后 (注意观察clear之后,remaining的变化)======");
System.out.println("remaining: " + remaining);
}
}
position: 0, limit: 1024, capacity: 1024
====== 调用put '123456' ======
position: 6, limit: 1024, capacity: 1024
======= 调用remaining方法之后 ======
remaining: 1018
======= 调用flip方法之后 ======
position: 0, limit: 6, capacity: 1024
======= 调用remaining方法之后 ======
remaining: 6
======= 调用get方法之后 ======
position: 1, limit: 6, capacity: 1024
======= 调用remaining方法之后 ======
remaining: 5
======= 调用clear方法之后 ======
position: 0, limit: 1024, capacity: 1024
======= 调用remaining方法之后 ======
remaining: 1024
Process finished with exit code 0