Java NIO(2) Buffer 缓冲器详解
Java NIO
4. Java NIO 核心组件:Selector 选择器,Pipe 管道
Buffer 缓冲器
Java NIO 的 Buffer 由数据和索引(用于高效访问和操纵数据)组成,这些索引包括4个:
- mark(标记索引)
- position(位置索引)
- limit(限制索引)
-
capacity(容量索引);
这 4 个索引的数值关系:0 <= mark <= position <= limit <= capacity,默认的初始化值:0
= position ,limit = capacity
唯一与通道 Channel 进行交互的 Buffer 是ByteBuffer,其他 Buffer 类似都会转化为 ByteBuffer 之后再与 Channel 进行交互,这些缓冲器类型包括以下:
CharBuffer、DoubleBuffer、IntBuffer、LongBuffer、ShortBuffer、FloatBuffer;
Buffer 常用的 API
ByteBuffer 常用的 API
缓存区的创建,写入,读取(以 ByteBuffer 示例)
//直接通过包装字节数组创建 ByteBuffer
ByteBuffer byteBuffer1 = ByteBuffer.wrap(new byte[]{21,23,5,98,123,34,22});
ByteBuffer byteBuffer2 = ByteBuffer.wrap("are you ok".getBytes("UTF-8"));
//创建一个指定容量的空白 ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//向 ByteBuffer 写入数据
byteBuffer.put((byte)12);
byteBuffer.putChar('a');
byteBuffer.putDouble(23.33);
byteBuffer.putLong(23333333333333L);
byteBuffer.put("are you ok?".getBytes("UTF-8"));
//从 ByteBuffer 读取数据
byteBuffer.flip(); //重绕缓冲区,limit = position,position = 0
byte b = byteBuffer.get(); //b = 12
char ch = byteBuffer.getChar(); //ch = a
double d = byteBuffer.getDouble(); //d = 23.33
long l = byteBuffer.getLong(); //l = 23333333333333
byte[] strbytes = new byte[byteBuffer.remaining()]; //byte数组大小 = byteBuffer position 到 limit 的元素个数
byteBuffer.get(strbytes);
String str = new String(strbytes); // str = "are you ok?"
缓冲区的各种刷新方式
①clear:清除此缓冲区,各个索引的设置:position = 0,limit = capacity ,mark 丢弃(即将 position,limit,mark 还原为初始状态);
// clear 常常用于要完全初始化一个 buffer 的时候
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileChannel in = new FileInputSteam("data.dat").getChannel();
in.read(buffer);
....
FileChannel in2 = new FileInputStream("data2.dat").getChannel();
buffer.clear(); //将 buffer 恢复到初始化状态
in2.read(buffer);
②flip:反转此缓冲区,各个索引的设置:limit = position,position = 0;
//filp 经常用于当缓冲区写入某些数据之后,position 发生变化,要重新读取缓冲区的有效内容(认为 limit 为当前的 position)
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("are you ok?".getBytes("UTF-8"));'
buffer.flip(); //设置缓冲区 position 后反转缓冲区
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String str = new String(bytes);
③rewind:重绕此缓冲区,各个索引的设置:position = 0,,mark 丢弃;
//rewind 常常用在缓冲区的 position 发生变化后,要重新使用缓冲区的有效内容(在缓冲区设置了limit的情况下),常常和 flip 配合使用
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("are you ok?".getBytes("UTF-8"));'
buffer.limit(buffer.position()).rewind(); //设置缓冲区 limit 后 rewind 缓冲区
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String str = new String(bytes);
④reset:将此缓冲区的位置重置为以前标记的位置,position = mark;
// reset 常常和 mark 结合使用,经常用在缓冲区回滚
FileChannel in = .....
ByteBuffer buffer = ByteBuffer.allocate(100);
in.read(buffer);
buffer.mark();
in.read(buffer);
buffer.reset(); //position 回滚到 mark 点,相当于第 6 行的 in.read() 没有实际效果
改变 Buffer 的字节存放顺序
Buffer 由以下 2 种字节存放顺序,默认使用 高位优先 储存顺序,可以通过order()方法改变字节存放顺序;
-
高位优先(big endian):将重要的字节存放在地址的最低储存单元,如 short 值 97 的高位优先排序:00000000 01100001
-
低位优先(little endian):将重要的字节存放在地址的最高储存单元,如 short 值 97 的低位优先排序:01100001 0000000
ByteBuffer buff = ByteBuffer.wrap(new byte[]{97});
buff.order(ByteOrder.BIG_ENDIAN); //更改为高位优先排序
buff.order(ByterOrder.LITTLE_EBDIAN); //更改为低位优先排序
使用视图缓冲器将 ByteBuffer 映射为其他类型 Buffer
可以使用视图 view buffer 以某个特定的基本类型 Buffer 的视窗查看底层的 ByteBuffer,ByteBuffer 仍然是实际出储存数据的地方,但是可以以其他类型的 Buffer(IntBudder,CharBuffer等)查看,而对这些视窗的任何修改都会映射为底层 ByteBuffer 中数据的修改;
ByteBuffer buff = ByteBuffer.wrap(new byte[]{0,1,0,'a'});
IntBuffer intbuf = buff.asIntBuffer();
CharBuffer charbuf = buff.asCharBuffer();
DoubleBuffer doublebuf = buff.asDoubleBuffer();
FloatBuffer floatbuf = buff.asFloatBuffer();
ShortBuffer shortbuf = buff.asShortBuffer();
ByteBuffer buff_readOnly = buff.asReadOnlyBuffer();
ByteBuffer 和 CharBuffer 之间的转换
对于将 ByteBuffer 转化为 CharBuffer ,会涉及到字符的编码解码问题,java.nio.CharSet对这一过程提供了支持;
1)对输入内容进行解码
//从文件通道获取ByteBuffer缓冲区,将其解码为Unicode字符类型的CharBuufer缓冲区,并输入到内存中;
ByteBuffer buff = ButtBuffer.allocate(1024);
FileChannel fc = new FileChannel(new InpuStream("target.txt")).getChannel();
fc.read(buff); //读取文件通道的数据到缓冲区(此时缓冲区中的数据时未被编码的byte)
buff.flip();
CharBuffer charbuff = CharSet.forname("UTF-8").decode(buff) //使用 UTF-8 格式解码
while(charbuff.hasRemaining())
System.out.print(charbuff.get());
2)对输出内容进行编码
//将Unicode字符类新的CharBuffer缓冲区编码为ByteBuffer缓冲区,并输出到文件中通道中;
FileChannel fc = new FileChannel(new OutputStream("src.txt")).getChannel();
CharBuffer charbuff = ByteBuffer.allcoate(1024).asCharBuffer();
charbuff.put("are you ok?");
charbuff.flip();
ByteBuffer buff = CharSet.forname("UTF-8").encode(charbuff); //使用 UTF-8 格式编码
fc.write(buff);