1.简介
Buffer是一种用于特定的基本类型数据的容器,是特定的基本类型元素的线性、有限序列。
1.1 Buffer有四个相当重要的属性:
1)capacity:容量,Buffer所能够容纳的基本类型数据的数量。
2)mark:标记,Buffer中的一个位置
3)position:位置,下一个读写操作的元素在Buffer中的位置。
4)limit:限制,禁止读写操作的第一个元素的索引
1.2 四个属性之间的关系
0 <= 标记 <= 位置 <= 限制 <= 容量
1.3 影响四个属性的相关操作
1)public final Buffer mark()
在Buffer的位置设置标记
2)public final Buffer reset()
将Buffer的位置设置为之前的标记值,如果标志不存在则抛出InvalidMarkException
3)public final Buffer position(int newPosition)
设置Buffer的位置,如果标记已定义并且大于新的位置,则要丢弃该标记
newPosition范围[0,limit],否则抛出IllegalArgumentException
4)public final Buffer limit(int newLimit)
设置此缓冲区的限制。
如果位置大于新的限制,则它被设置为此新限制。
如果标记已定义并且大于新限制,则要丢弃该标记。
newPosition范围[0,capacity],否则抛出IllegalArgumentException
5)public final Buffer flip()
反转此缓冲区。
首先对当前位置设置限制,然后将该位置设置为零。
如果已定义了标记,则丢弃该标记。
效果等同于Buffer.limit(Buffer.position()); + Buffer.position(0);
6)public final Buffer rewind()
重绕此缓冲区。将位置设置为零并丢弃标记。
效果等同于Buffer.position(0);
7)public final Buffer clear()
清除此缓冲区。将位置设置为零,限制设置为该容量,并且丢弃标记。
注:此方法不能实际擦除缓冲区中的数据
效果等同于Buffer.limit(Buffer.capacity()); + Buffer.position(0);
8)put/get等当前位置存取方法均会使位置向前移动N个单位,存取操作的位置不能大于限制
9)put/get等绝对位置存取方法均不会改变Buffer的位置和限制
2.个人总结
1)关于写
Buffer通过position来控制读写操作的元素位置,每次写操作后都会将position向后移动写入数据的长度,因此一个连续的写操作的作用范围为从第1个写操作的position1到第N个写操作的positionN,即[position1, positionN+length(dataN))且0<=position1<positionN+length(dataN)<=limit。
2)关于读
由写的过程可知写的内容位于[position1, positionN+length(dataN))之间,所以读取写内容的时候也必须读取该范围的内容。
3)程序使用
程序使用Buffer时通过每次记录写操作的position1和positionN+length(dataN)不可行,因为可能对象A对Buffer进行写操作,并且只返回Buffer对象给需要读Buffer的对象B。
所以要利用Buffer的属性进行读写操作,使得
每次写操作开始前使postion=0,limit=capacity,这样写操作范围为[0, limit) = [0, capacity),即调用方法clear()
在写操作结束后读取内容前使limit=position,postion=0,这样读操作范围为[0, limit) = [position1, positionN+length(dataN)),即调用方法flip()
第一次读取内容后要重复读取内容前只需使postion=0,这样读操作范围为[0, limit) = [position1, positionN+length(dataN)),即调用方法rewind()
3.实例
package com.siyuan.test.jdk.nio;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.InvalidMarkException;
public class BufferTest {
/**
* @param args
*/
public static void main(String[] args) {
int BSIZE = 1024;
ByteBuffer bBuffer = ByteBuffer.allocate(BSIZE);
System.out.println("初始化...");
System.out.println(getBufferStatus(bBuffer));
bBuffer.putChar('a');
System.out.println("在位置写入字符a后");
System.out.println(getBufferStatus(bBuffer));
bBuffer.mark();
bBuffer.putChar('b');
System.out.println("在位置设置标记,写入字符b后");
System.out.println(getBufferStatus(bBuffer));
int lastPosition = bBuffer.position(); //记录缓冲区有效字符的终结位置
bBuffer.reset(); //效果等同于bBuffer.position(2);
System.out.println("将位置设置为标志后");
System.out.println(getBufferStatus(bBuffer));
//读数据
//设置位置,如果标记已定义并且大于新的位置,则要丢弃该标记
bBuffer.position(lastPosition);
/*
*将限制设置为位置,然后将位置设置为0,如果已定义了标记,则丢弃该标记
*常用于向buffer中写入数据后和从buffer中读取数据前
*效果等同于
*bBuffer.limit(bBuffer.position());
*bBuffer.position(0);
*/
bBuffer.flip();
System.out.println("还原位置,flip后");
System.out.println(getBufferStatus(bBuffer));
while (bBuffer.hasRemaining()) {
System.out.print(bBuffer.getChar());
}
System.out.println("读取完位置和限制之间的数据后");
System.out.println(getBufferStatus(bBuffer));
//重新读取
/*
* 重绕此缓冲区。将位置设置为零并丢弃标记。
* 效果等同于
* Buffer.position(0);
*/
bBuffer.rewind();
System.out.println("rewind后");
System.out.println(getBufferStatus(bBuffer));
while (bBuffer.hasRemaining()) {
System.out.print(bBuffer.getChar());
}
System.out.println("读取完位置和限制之间的数据后");
System.out.println(getBufferStatus(bBuffer));
//重新写入数据
/*
* 清除此缓冲区。将位置设置为零,限制设置为该容量,并且丢弃标记。
* 注:此方法不能实际擦除缓冲区中的数据
* 通过position和limit来确定有效数据的存储区域
* 效果等同于
* bBuffer.limit(buffer.capacity());
* bBuffer.position(0);
*/
bBuffer.clear();
System.out.println("clear后");
System.out.println(getBufferStatus(bBuffer));
bBuffer.putChar('b');
bBuffer.putChar('b');
bBuffer.putChar('c');
bBuffer.putChar('c');
System.out.println("在位置写入bbcc后");
System.out.println(getBufferStatus(bBuffer));
/*
* 绝对位置读取,不影响Buffer的属性
*/
System.out.println(bBuffer.getChar(4));
System.out.println("在绝对位置4读取字符后");
System.out.println(getBufferStatus(bBuffer));
/*
* 绝对位置写入,不影响Buffer的属性,
* 且写入方式为覆盖,而不是插入
*/
bBuffer.putChar(4, 'd');
System.out.println("在绝对位置4写入字符d后");
System.out.println(getBufferStatus(bBuffer));
System.out.println("重新读取Buffer的内容");
bBuffer.flip();
while (bBuffer.hasRemaining()) {
System.out.print(bBuffer.getChar());
}
}
public static final String getBufferStatus(Buffer buffer) {
StringBuilder str = new StringBuilder();
str.append("容量:").append(buffer.capacity())
.append(",位置:").append(buffer.position())
.append(",限制:").append(buffer.limit());
boolean markExist = false;
try {
int position = buffer.position();
buffer.reset();
markExist = true;
buffer.position(position);
} catch(InvalidMarkException e) {
}
str.append(",存在标志: ").append(markExist);
return str.toString();
}
}
运行结果:
初始化...
容量:1024,位置:0,限制:1024,存在标志: false
在位置写入字符a后
容量:1024,位置:2,限制:1024,存在标志: false
在位置设置标记,写入字符b后
容量:1024,位置:4,限制:1024,存在标志: true
将位置设置为标志后
容量:1024,位置:2,限制:1024,存在标志: true
还原位置,flip后
容量:1024,位置:0,限制:4,存在标志: false
ab读取完位置和限制之间的数据后
容量:1024,位置:4,限制:4,存在标志: false
rewind后
容量:1024,位置:0,限制:4,存在标志: false
ab读取完位置和限制之间的数据后
容量:1024,位置:4,限制:4,存在标志: false
clear后
容量:1024,位置:0,限制:1024,存在标志: false
在位置写入bbcc后
容量:1024,位置:8,限制:1024,存在标志: false
c
在绝对位置4读取字符后
容量:1024,位置:8,限制:1024,存在标志: false
在绝对位置4写入字符d后
容量:1024,位置:8,限制:1024,存在标志: false
重新读取Buffer的内容
bbdc
4.参考资料
JDK文档