天天看点

Java NIO 详解(一)

i/o即输入输出,是计算机与外界世界的一个借口。io操作的实际主题是操作系统。在java编程中,一般使用流的方式来处理io,所有的io都被视作是单个字节的移动,通过stream对象一次移动一个字节。流io负责把对象转换为字节,然后再转换为对象。

nio即new io,这个库是在jdk1.4中才引入的。nio和io有相同的作用和目的,但实现方式不同,nio主要用到的是块,所以nio的效率要比io高很多。

nio和io最大的区别是数据打包和传输方式。io是以流的方式处理数据,而nio是以块的方式处理数据。

面向流的io一次一个字节的处理数据,一个输入流产生一个字节,一个输出流就消费一个字节。为流式数据创建过滤器就变得非常容易,链接几个过滤器,以便对数据进行处理非常方便而简单,但是面向流的io通常处理的很慢。

面向块的io系统以块的形式处理数据。每一个操作都在一步中产生或消费一个数据块。按块要比按流快的多,但面向块的io缺少了面向流io所具有的有雅兴和简单性。

channel是对原io中流的模拟,任何来源和目的数据都必须通过一个channel对象。一个buffer实质上是一个容器对象,发给channel的所有对象都必须先放到buffer中;同样的,从channel中读取的任何数据都要读到buffer中。

buffer是一个对象,它包含一些要写入或读出的数据。在nio中,数据是放入buffer对象的,而在io中,数据是直接写入或者读到stream对象的。应用程序不能直接对 channel 进行读写操作,而必须通过 buffer 来进行,即 channel 是通过 buffer 来读写数据的。

在nio中,所有的数据都是用buffer处理的,它是nio读写数据的中转池。buffer实质上是一个数组,通常是一个字节数据,但也可以是其他类型的数组。但一个缓冲区不仅仅是一个数组,重要的是它提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

使用 buffer 读写数据一般遵循以下四个步骤:

写入数据到 buffer;

调用 flip() 方法;

从 buffer 中读取数据;

调用 clear() 方法或者 compact() 方法。

当向 buffer 写入数据时,buffer 会记录下写了多少数据。一旦要读取数据,需要通过 flip() 方法将 buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 buffer 的所有数据。

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或 compact() 方法。clear() 方法会清空整个缓冲区。compact() 方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

buffer主要有如下几种:

Java NIO 详解(一)

channel是一个对象,可以通过它读取和写入数据。可以把它看做io中的流。但是它和流相比还有一些不同:

channel是双向的,既可以读又可以写,而流是单向的

channel可以进行异步的读写

对channel的读写必须通过buffer对象

正如上面提到的,所有数据都通过buffer对象处理,所以,您永远不会将字节直接写入到channel中,相反,您是将数据写入到buffer中;同样,您也不会从channel中读取字节,而是将数据从channel读入buffer,再从buffer获取这个字节。

因为channel是双向的,所以channel可以比流更好地反映出底层操作系统的真实情况。特别是在unix模型中,底层操作系统通常都是双向的。

Java NIO 详解(一)

在java nio中channel主要有如下几种类型:

filechannel:从文件读取数据的

datagramchannel:读写udp网络协议数据

socketchannel:读写tcp网络协议数据

serversocketchannel:可以监听tcp连接

io中的读和写,对应的是数据和stream,nio中的读和写,则对应的就是通道和缓冲区。nio中从通道中读取:创建一个缓冲区,然后让通道读取数据到缓冲区。nio写入数据到通道:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入。

我们已经知道,在nio系统中,任何时候执行一个读操作,您都是从channel中读取,而您不是直接从channel中读取数据,因为所有的数据都必须用buffer来封装,所以您应该是从channel读取数据到buffer。

因此,如果从文件读取数据的话,需要如下三步:

从fileinputstream获取channel

创建buffer

从channel读取数据到buffer

下面我们看一下具体过程:

第一步:获取通道

第二步:创建缓冲区

第三步:将数据从通道读到缓冲区

类似于从文件读数据,

第一步:获取一个通道

第二步:创建缓冲区,将数据放入缓冲区

第三步:把缓冲区数据写入通道中

copyfile是一个非常好的读写结合的例子,我们将通过copyfile这个实力让大家体会nio的操作过程。copyfile执行三个基本的操作:创建一个buffer,然后从源文件读取数据到缓冲区,然后再将缓冲区写入目标文件。

上面程序中有三个地方需要注意

当没有更多的数据时,拷贝就算完成,此时 read() 方法会返回 -1 ,我们可以根据这个方法判断是否读完。

position:跟踪已经写了多少数据或读了多少数据,它指向的是下一个字节来自哪个位置

limit:代表还有多少数据可以取出或还有多少空间可以写入,它的值小于等于capacity。

capacity:代表缓冲区的最大容量,一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的。

flip、clear这两个方法便是用来设置这些值的。

我们先看一下flip的源码:

Java NIO 详解(一)

在上面的filecopy程序中,写入数据之前我们调用了<code>buffer.flip();</code>方法,这个方法把当前的指针位置position设置成了limit,再将当前指针position指向数据的最开始端,我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。

先看一下clear的源码:

Java NIO 详解(一)

在上面的filecopy程序中,写入数据之后也就是读数据之前,我们调用了 <code>buffer.clear();</code>方法,这个方法重设缓冲区以便接收更多的字节。上图显示了在调用 clear() 后缓冲区的状态。

下一篇: Java IO