Java NIO
“对语言设计人员来说,创建好的输入/输出系统是一项特别困难的任务。”
――《Think in Java》
下面一起来研究一下NIO的用法,先来一段示例代码:
1 | public class NioTest1 { |
以上代码会输出10个随机数,输出很简单,但是包含的逻辑却很丰富。
java的io现在分为2种:
- java.io
- java.nio
java.io中最为核心的一个概念是流(Stream),面向流的编程,里面也大量运用了装饰模式,在java.io中。Java中一个流要么是输入流,要么是输出流,不可能同时既是输入流又是输出流。java.nio中拥有3个核心概念:Selector,Channel与Buffer。在java.nio中,我们是面向块(block)或是缓冲区(buffer)编程的。Buffer本身就是块内存,底层实现上,它实际上是个数组。数据的读、写都是通过Buffer来实现的。
除了数组之外,Buffer还提供了对于数据的结构化访问方式,并且可以追踪到系统的读写过程。Java中的7种原生数据类型都有各自对应的Buffer类型,如IntBuffer,LongBuffer,ByteBuffer及CharBuffer等等,并没有BooleanBuffer类型。
Channel
指的是可以向其写入数据或是从中读取数据的对象,它类似于java.io中的Stream。所有数据的读写都是通过Buffer来进行的,永远不会出现直接向Channel写入数据的情况,或是直接从Channel读取数据的情况。与Stream不同的是,Channel是双向的,一个流只可能是InputStream或是OutputStream,Channel打开后则可以进行读取、写入或是读写。由于Channel是双向的,因此它能更好的反映出底层操作系统的真实情况;在linux系统中,底层操作系统的通道就是双向的。
实例1
读取文件
NioTest2.txt
1 | hello world welcome |
1 | public class NioTest2 { |
实例2
写入文件
1 | public class NioTest3 { |
上面2个例子,能看出一定的模式,就是数据一定是跟Buffer打交道的,然后再读或写到Channel中。
读: file->channel->buffer->filp()->打印内容
写: 数据->buffer->flip()->channel->输出到文件
Buffer中的3个重要属性
看实例中都调用了一句关键代码filp()
,这个方法的源码如下:
1 | public final Buffer flip() { |
关于NIO Buffer中的3个重要状态属性的含义:position,limit与capacity。
- Buffer的capacity是指它所包含的元素的数量,capacity不可能为负数,也不会变化。
- Buffer的limit是指第一个不能被读或者写的元素的索引,并且永远不会超过capacity。
- Buffer的position是说下一个将要被读或者写的索引,不能为负数,并且不会超过limit。
假设执行Buffer.allocate(6),下面看内存划分情况。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
↑ | ↑ | |||||
position | capacity | |||||
limit |
最后一个是虚拟的位置。
现在往Buffer中放入2个元素,记住position是下一个可以被读或者写的位置,所以应该是在索引为2的地方。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
↑ | ↑ | |||||
position | capacity | |||||
limit |
再放2个元素。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
↑ | ↑ | |||||
position | capacity | |||||
limit |
现在调用flip()
,看看会发生什么事。
先把position归位,然后limit变为之前position的位置。
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
↑ | ↑ | ↑ | ||||
position | limit | capacity |
而且position是不会大于limit的,所以hasRemaining()
的实现其实也很简单,当
position和limit相等的时候,表示Buffer的东西已经被读取完毕。
1 | public final boolean hasRemaining() { |
实例3
1 |
|
通过NIO读取文件涉及到3个步骤:
- 从FileInputStream获取到FileChannel对象。
- 创建Buffer。
- 将数据从Channel读取到Buffer中。
绝对方法与相对方法
绝对方法与相对方法的含义:
- 相对方法:limit值与position值会在操作时被考虑到。
- 绝对方法:完全忽略掉limit值和position值。