20.4.1 为基于通道的I/O使用NIO(2)
下面是该程序的工作原理。首先,获取一个Path对象,其中包含test.txt文件的相对路径。将指向该对象的引用赋给filepath。接下来调用newByteChannel()方法,并传递filepath作为参数,获取链接到文件的通道。因为没有指定打开方式,所以为读取操作打开文件。注意这个通道是由带资源的try语句管理的对象。因此,当代码块结束时会自动关闭通道。然后该程序调用ByteBuffer的allocate()方法分配缓存,当读取文件时,缓存将容纳文件的内容。指向缓存的引用保存在mBuf中。然后调用read()方法将文件内容读取到mBuf中,读取的字节数量保存在count中。接下来调用rewind()方法回绕缓存。这个调用是必需的,因为在调用read()方法之后,当前位置位于缓存的末尾。为了通过get()方法读取mBuf中的字节,必须将当前位置重置到缓存的开头(记住,get()方法是由ByteBuffer定义的)。因为mBuf是字节缓存,所以get()方法返回的值是字节。将它们强制转换为char类型,从而可以作为文本显示文件(也可以创建将字节编码成字符的缓存,然后读取该缓存)。当到达文件末尾时,read()方法返回的值将是.1。当到达文件末尾时,自动关闭通道,结束程序。
有趣的一点是:注意程序在一个try代码块中获取Path,然后使用另外一个try代码块获取并管理与这个路径链接的通道。尽管使用这种方式没有什么错误,但是在许多情况下,可以对其进行简化,从而只使用必需的try代码块。在这种方式中,Paths.get()和newByteChannel()方法调用被连接到一起。例如,下面是对该程序进行改写后的版本,该版本使用这种方式:
在这个版本中,不再需要filepath变量,并且两个异常都通过相同的try语句进行处理。因为这种方式更紧凑,所以在本章的其他例子中也将使用这种方式。当然,在您自己的代码中可能会遇到以下这种情况:不能将Path对象的创建和通道的获取放到一起。对于这种情况,可以使用前一种方式。
读取文件的另外一种方式是将文件映射到缓存。这种方式的优点是缓存自动包含文件的内容,不需要显式的读操作。为了映射和读取文件内容,需要遵循以下一般过程。首先,像前面介绍的那样,获取用于封装文件的Path对象。接下来调用Files.newByteChannel()方法,并传递获取的Path对象作为参数,获取链接到文件的通道,然后将返回的对象转换成FileChannel类型。如前所述,newByteChannel()方法返回SeekableByteChannel类型的对象。当使用默认文件系统时,可以将这个对象转换成FileChannel类型。然后,在通道上调用map()方法,将通道映射到缓存。map()方法是由FileChannel定义的,所以需要将返回的对象转换成FileChannel类型。map()方法如下所示:
- MapMode.READ_ONLY MapMode.READ_WRITE MapMode.PRIVATE
对于读取文件,使用MapMode.READ_ONLY。要读取并写入文件,使用MapMode.READ_WRITE。MapMode.PRIVATE导致创建文件的私有副本,并且对缓存的修改不会影响底层的文件。文件中开始进行映射的位置是由pos指定的,并且映射的字节数量是由size指定的。作为MappedByteBuffer返回指向缓存的引用,MappedByteBuffer是ByteBuffer的子类。一旦将文件映射到缓存,就可以从缓存读取文件了。下面是演示这种方式的一个例子:
|