位于上海,服务全国!

位于上海,服务全国!

Java NIO.2文件处理

作者:admin 分类: 时间:2017-04-07 09:53:24 点击量:1035

Java NIO的主要用途是通过通道和缓冲区访问文件。 NIO.2也将其扩展到基于流的I / O。 因此,NIO现在提供基于通道的I / O,基于流的I / O以及路径和文件系统操作的能力。 添加到NIO.2的新功能广泛扩展了其前身的能力。 本文将介绍基于通道I / O的功能,并以简短方式介绍其的用途。

概述
Java NIO系统基于两个基本结构,被称为缓冲区和通道。 缓冲区提供了一个分段区域,其内数据以后被存储和检索。 它作为I / O传输之间的临时存储库。 NIO缓冲区与通道联手配合;如果数据在通过信道的途中前行,则缓冲区表示携带数据的载体。 通道表示与I / O设备的打开连接,如文件,插座等。 想象成一个气动管,其中在与通道源相对应的缓冲器中写入数据,并且在管的另一端创建一个响应缓冲区,以便可以将其排出以从接收端获得答复。 因此,缓冲器通过通道的开放连接作为载体。


改进的NIO.2功能包括更多,而不只是通道。 它现在也可以用于基于流的I / O。 但这并不意味着基于通道I / O的重要性较小。 事实上,NIO.2引入的异步通道API是旧通道的扩展。 异步通道是通过使用单独的线程并行支持多个I / O操作的连接。 此功能特别提高了多线程I / O操作的性能。 与此相反,同步I / O在每个I / O连接模式下以一个线程运行。 因此,它在可扩展性和性能方面都有局限性。 构建异步I / O API以克服重叠I / O的这种限制,这特别适合于花费大量时间的操作。 然而,这不会使同步I / O API被废弃;相反,它专门用于单线程,相对较快的I / O操作。

使用基于通道的I / O进行读操作
为了更好地了解基于通道的I / O,我们执行一个简单的Java程序。 因为最常见的I / O操作是在磁盘文件上执行的,所以我们将使用一个普通文件来执行I / O操作。

但是,在执行任何文件I / O之前,我们首先必须访问该文件。 这可以通过创建一个路径对象来实现。 路径是一个接口,其实例用于在文件系统中定位文件。 因此,我们可以调用在Paths类中定义的静态方法get()。 该类只包含两个返回Path对象的重载静态方法;一个转换路径字符串,另一个使用URI。

static Path get(String first, String ...more)
static Path get(URI uri)
一旦我们访问了文件的文件描述符,我们可以根据需要以各种方式打开它。 例如,如果我们要在异步通道中读/写字节,我们可能会创建一个AsynchronousByteChannel的实例。 如果我们要创建一个连接到包含可变长度字节序列的文件的可寻址字节通道,我们可以创建一个SeekableByteChannel的实例。 还有其他的,如GatheringByteChannel,它创建一个通道以便从缓冲区序列ScatteringByteChannel,MulticastChannel等中写入字节。 有关它们的详细信息,请参阅Java API文档。

现在,为了创建一个SeekableByteChannel实例,我们可以调用在java.nio.files包的Files类中定义的名为newByteChannel()的静态方法。

文件通道操作是基于字节的。 因此,我们将使用的缓冲区称为ByteBuffer。 ByteBuffer使用在创建时提供容量而指定的后台阵列。 通道的read()方法从给定通道读取一个字节序列,并将其放在后台阵列中。 一旦读取完成后,必须倒缓缓冲区,使缓冲区的当前位置设置为第0个位置。 然后可以通过其索引位置读取字节数据。

以下代码说明了这个概念。

public static void readFile(String filePath) {
   try (SeekableByteChannel channel =
         Files.newByteChannel(Paths.get(filePath))) {
      ByteBuffer buf = ByteBuffer.allocate(255);
      int c = 0;
      do {
         // Reads a sequence of bytes from this
         // channel into the given
         // buffer the position is updated with
         // the number of bytes actually read
         c = channel.read(buf);


         if (c != -1) {
            // Rewinds buffer, the position is set to zero
            buf.rewind();
            for (int i = 0; i < c; i++)
               System.out.print(buf.get(i));
         }
      } while (c != -1);
      System.out.println();
   } catch (IOException | InvalidPathException ex) {
      System.err.println(ex);
   }
}
使用基于通道的I / O进行写操作
写操作几乎与前面所述的读操作相同。 这个概念是在写入模式下创建一个通道。 因为这里也是为了这个目的而使用的一个磁盘文件,一个文件被打开,并且选择WRITE和CREATE。 在枚举java.nio.file.StandardOpenOption中定义了各种这样的选项。其中一些如下:

APPEND: 文件以附加模式打开
CREATE: 创建一个文件,如果它不存在
CREATE_NEW: 如果文件已经存在,则创建一个新文件会失败
DELETE_ON_CLOSE: 文件关闭后删除
READ: 文件以读取模式打开
WRITE: 文件以写入方式打开
通过通道读取和写入数据有几种方法。 所示代码片段只是其中的一些。 下一个示例显示,我们已经打开通道,通过调用newByteChannel()方法,将其类型转换为FileChannel对象。 根据数据的长度分配缓冲区。 调用缓冲区的rewind()方法在第一个存储单元来设置当前位置。 然后,以缓冲对象为参数调用通道的write()方法。 最后,缓冲区的内容被写入指定的文件。


public static void writeFile(String filePath, String data) {
   try (FileChannel channel = (FileChannel)
      Files.newByteChannel(Paths.get(filePath),
   StandardOpenOption.CREATE,
   StandardOpenOption.WRITE)) {
      ByteBuffer buf = ByteBuffer.allocate(data.length());
      for (int i = 0; i < data.length(); i++) {
         buf.put((byte) (data.charAt(i) + ' '));
      }
      // Rewinds buffer, the position is set to zero
      buf.rewind();
      channel.write(buf);
   } catch (InvalidPathException | IOException ex) {
      System.err.println(ex);
   }
}
与基于流的I/O进行快速对比
值得一提的是,虽然基于流的I/O类似于基于通道的I/O,但基于通道的I / O架构至少改变了三种方式缓慢处理面向流的I/O方案:

首先,与基于流的I/O单向读或写操作相比,基于通道的I/O支持双向读写操作。
现在可以通过通道执行异步读写操作。
通道始终使用一个缓冲区进行读写操作。在实现任何读写操作之前,发送到通道的数据必须存储在一个缓冲区中。 后备缓冲区可以方便地与多个线程一起使用。
结论
如果你已经用过NIO,你可能注意到NIO.2具有Path接口的明显优势。 该功能在上一个版本已经没有了。 我们使用的try-with资源也是后JDK 7合并。 NIO.2扩展了基于通道的I/O能力,不仅方便易于编码,而且使I/O操作更具效率。