Java NIO(2) Buffer 缓冲器详解

Java NIO

1. Java NIO 基本组件介绍

2. Java NIO 核心组件:Buffer 缓冲器

3. Java NIO 核心组件:Channel 通道

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

Java NIO(2) Buffer 缓冲器详解_第1张图片

ByteBuffer 常用的 API

Java NIO(2) Buffer 缓冲器详解_第2张图片


缓存区的创建,写入,读取(以 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?"

缓冲区的各种刷新方式

Java NIO(2) Buffer 缓冲器详解_第3张图片
①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);


你可能感兴趣的:(Java,NIO,NIO,缓冲器,Buffer,ByteBuffer,Java,Java,I/O,系统)