Java-NIO入门篇

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

比较

Old IO NIO
用流以字节为单位进行操作 以数据块为单位进行操作
优点:使用简单 优点:速度快
缺点:速度慢 缺点:使用比流要稍复杂,编程不像流那样的优雅
流是单向的,InputStream完成读,OutputStream完成写 通道是双向的

提示:在JDK1.4开始引入NIO类库(java.nio.)后,对原来的java.io. 以NIO为基础进行了修改,因此速度上会更快。

NIO概念

  • Bufferr (缓冲区):用数组实现的缓存数据的对象。
  • Channel (通道):也可以称之为管道。可以想像成流水的水管,水就是我们的数据。

下面两个图是NIO读/写数据时的示意图:
图1-NIO读数据示意图

图2-NIO写数据示意图

Buffer的三个状态变量

position(位置点)。跟踪当前已经读/写了多少数据,例如:在读取数据时,position=3,表示已经从Buffer读取三个元素值,当前指针指向第四个元素(即下一个读取的元素的位置);在写数据时,position=3,表示已经向Buffer写入了三个元素,指针指向第四个元素的位置(即下一个写入元素的位置)。

  • limit(限制值)。指定缓冲区允许读取/写入多少无素,默认情况下 limit=capacity。
  • capacity(容量)。

关系:position <= limit <= capacity

例:
1)定义一个容量为9的Buffer:ByteBuffer buffer = new ByteBuffer(9);
图3-初始化状态下的Buffer

2)向Buffer写入两个元素:buffer.put(x); buffer.put(y);
图4-写入两个元素后的Buffer

3)执行flip()方法:buffer.flip();
将limit设置为position,然后将position设置为 0。如果已定义了标记,则丢弃该标记。
图5-执行flip()方法后的Buffer

4)执行clear()方法:buffer.clear();
注意:clear()方法不能实际清除缓冲区中的数据。只是将position设置为 0,将limint设置为容量,并丢弃标记。
图6-执行clear()方法后的Buffer

完整示例代码:

1
2
3
4
5
6
ByteBuffer buff = ByteBuffer.allocate(9); // position=0, limit=9, capacity=9
buff.put((byte) 'a') // position=1, limit=9, capacity=9
.put((byte) 'b'); // position=2, limit=9, capacity=9
buff.flip(); // position=0, limit=2, capacity=9
buff.clear(); // position=0, limit=9, capacity=9
buff = null;

Buffer的分配包装与分片

缓冲区既可以直接生成,也可以包装一个字节数组获得。
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
* 先分配内存,再赋值。
*/
ByteBuffer buff = ByteBuffer.allocate(9);
for (int i = 0; i < buff.capacity(); i++) {
buff.put((byte) i);
}// 到此buff的内容为 [0, 1, 2, 3, 4, 5, 6, 7, 8]
/*
* 直接将一个数组包装成缓冲区。
*/
byte[] b = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8};
ByteBuffer buff2 = ByteBuffer.wrap(b); // 到此buff2的内容为 [0, 1, 2, 3, 4, 5, 6, 7, 8]
/*
* 分片操作。
*/
buff2.position(2);
buff2.limit(6);
ByteBuffer slice = buff2.slice();
b[2] *= 11; // buff2 和 slice 的内容都同时跟随改变,因为他们是共享一块内存区。
slice.put(1, (byte) (((int) slice.get(1)) * 11)); // buff2 和 slice 的内容都同时跟随改变,因为他们是共享一块内存区。