Java NIO 由以下几个核心部分组成:
虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。
Buffers 缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。所谓“输入/输出”讲的无非就是把数据移 进或移出缓冲区。
概念:实际上缓冲区就是一个包含在对象里的基本数据元素数组
属性:所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。它们是:
我们通过 ByteBuffer.allocate(11) 方法创建一个 10 个 byte 的数组缓冲区,初始状态如上图所示,position 的位置为 0,capacity 和 limit 默认都是数组长度。当我们写入 5 个字节时位置变化如下图所示:
这时如果我们想在不丢失位置的情况下进行一些修改,比如说将hello更改为MelloW
可以这样实现
buffer.put(0,(byte)'M').put((byte)'w');
修改后的Buffer图像如下图
如果我们想将缓冲区的 5 个字节数据写入 Channel 通信信道,我们需要调用 byteBuffer.flip() 方法,数组的状态又发生如下变化
还有一个方法 rewind (),会重置position的位置为0 但不改变limit的位置,可以使用rewind()后退,重读已经被翻转的缓冲区中的数据。
看下面代码
从上面例子可以看出clear后实际上buffer中数据还是存在的
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。
看下面代码:
compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
其它还有mark()与reset() equals()等方法 就不一一介绍了。
@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);![]()
- Channels
- Buffers
- Selectors
虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API。
Buffers 缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。所谓“输入/输出”讲的无非就是把数据移 进或移出缓冲区。
概念:实际上缓冲区就是一个包含在对象里的基本数据元素数组
属性:所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。它们是:
- 容量(Capacity) 缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
- 上界(Limit) 缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
- 位置(Position) 下一个要被读或写的元素的索引。位置会自动由相应的get( )和put( )函数更新。
- 标记(Mark) 一个备忘位置。调用mark( )来设定mark = postion。调用reset( )设定position = mark。标记在设定前是未定义的(undefined)。

我们通过 ByteBuffer.allocate(11) 方法创建一个 10 个 byte 的数组缓冲区,初始状态如上图所示,position 的位置为 0,capacity 和 limit 默认都是数组长度。当我们写入 5 个字节时位置变化如下图所示:

这时如果我们想在不丢失位置的情况下进行一些修改,比如说将hello更改为MelloW
可以这样实现
buffer.put(0,(byte)'M').put((byte)'w');
修改后的Buffer图像如下图

如果我们想将缓冲区的 5 个字节数据写入 Channel 通信信道,我们需要调用 byteBuffer.flip() 方法,数组的状态又发生如下变化

还有一个方法 rewind (),会重置position的位置为0 但不改变limit的位置,可以使用rewind()后退,重读已经被翻转的缓冲区中的数据。
- 一旦读完Buffer中的数据,需要让Buffer准备好再次被写入。可以通过clear()或compact()方法来完成。
- 如果调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。但这个时候Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。
看下面代码
- public static void main(String[] argv) throws Exception {
- CharBuffer buffer = CharBuffer.allocate(20);
- System.out.println("初始化时候:position="+buffer.position()+" limit="+buffer.limit());
- fillBuffer(buffer,"hello");
- System.out.println("填入hello以后:position="+buffer.position()+" limit="+buffer.limit());
- buffer.put(0,'M').put('w');
- System.out.println("修改成Mellow后:position="+buffer.position()+" limit="+buffer.limit());
- buffer.flip();
- System.out.println("翻转后:position="+buffer.position()+" limit="+buffer.limit());
- buffer.clear();
- System.out.println("clear后:position="+buffer.position()+" limit="+buffer.limit());
- buffer.position(0);
- while (buffer.hasRemaining()) {
- System.out.println(buffer.get());
- }
- }private static void fillBuffer(CharBuffer buffer) {
- String string = "MY TEST BUFFER";
- for (int i = 0; i < string.length(); i++) {
- buffer.put(string.charAt(i));
- }
- }
- 结果:
- 初始化时候:position=0 limit=20
- 填入hello以后:position=5 limit=20
- 修改成Mellow后:position=6 limit=20
- 翻转后:position=0 limit=6
- clear后:position=0 limit=20
- M
- e
- l
- l
- o
- w
从上面例子可以看出clear后实际上buffer中数据还是存在的
如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先先写些数据,那么使用compact()方法。
看下面代码:
- public static void compactTest(){
- CharBuffer buffer = CharBuffer.allocate(11);
- fillBuffer(buffer,"hello world");
- buffer.position(6);
- buffer.compact();
- buffer.put("j");
- buffer.put("a");
- buffer.put("v");
- buffer.put("a");
- buffer.flip();
- while (buffer.hasRemaining()) {
- System.out.print(buffer.get());
- }
- }
- 打印结果为worldjava
compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
其它还有mark()与reset() equals()等方法 就不一一介绍了。
@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);