20.4.1 为基于通道的I/O使用NIO(3)
2. 通过通道写入文件
与读取文件一样,使用通道将数据写入文件的方式也有多种。首先介绍最常用的一种。在这种方式中,手动分配缓存,将数据写入到缓存中,然后执行显式的写操作,将数据写入到文件中。
在向文件中写入数据之前,必须打开文件。为此,首先获取描述文件的Path对象,然后使用Path对象打开文件。在这个例子中,将为进行基于字节的输出打开文件,通过显式的输出操作写入数据。所以,这个例子调用Files.newByteChannel()方法来打开文件,并建立链接到文件的通道。正如前面所显示的,newByteChannel()方法的一般形式如下:
- static SeekableByteChannel newByteChannel(Path path, OpenOption ... how)
- throws IOException
该方法返回的SeekableByteChannel对象中封装了用于文件操作的通道。为了针对输入操作打开文件,how参数必须为StandardOpenOption.WRITE。当文件不存在时,如果希望创建文件,就必须指定StandardOpenOption.CREATE(也可以使用表20-7中显示的其他选项)。在前面解释过,SeekableByteChannel是接口,用于描述能够用于文件操作的通道。FileChannel类实现了该接口。如果使用的是默认文件系统,可以将返回对象转换成FileChannel类型。当通道使用完之后必须关闭。
下面是通过通道写入文件的一种方式,这种方式显式调用write()方法。首先,调用newByteChannel()方法以获取与文件链接的Path对象,然后打开文件,将返回的结果转换成FileChannel类型。接下来分配字节缓存,并将数据写入到缓存中。在将数据写入到文件之前,在缓存上调用rewind()方法,将当前位置设置为0(在缓存上的每次输出操作都会增加当前位置。因此在写入文件之前,必须重置当前位置)。然后,对通道调用write()方法,传递缓存。下面的程序演示了这个过程。该程序将字母表写入到test.txt文件中。
该程序有一个重要方面值得强调。如前所述,在将数据写入mBuf中之后,但是在将数据写入文件中之前,对mBuf调用rewind()方法。在将数据写入mBuf中之后,为了将当前位置重置为0,这是必须做的。请记住,每次对mBuf调用put()方法都会向前推进当前位置。所以在调用write()方法之前,需要将当前位置重置到缓存的开头。如果没有这么做,write()方法会认为缓存中没有数据。
在输入和输出操作之间,处理缓存重置的另外一种方式是调用flip()方法而不是调用rewind()方法。flip()方法将当前位置设置为0,并将界限设置为当前位置之前。在前面的示例中,因为缓存的容量等于界限,所以可以使用flip()方法而不是rewind()方法。然而,并不是在所有情况下都可以互换这两个方法。
通常,在读和写操作之间必须重置缓存。例如,对于前面的例子下面的循环会将字母表写入文件3次。请特别注意对rewind()方法的调用。