基石–Path
Path 既可以表示一个目录,也可以表示一个文件,就像 File 那样——当然了,Path 就是用来取代 File 的。
具体代码如下:
1 | package main.java.com.study.nio; |
如果要将 File 转换为 Path,可以通过 File 类的 toPath()
方法完成。代码示例如下:
1 | File file = new File("nioTest.txt"); |
如果要将 Path 转换为 File,可以通过 Path 类的 toFile()
方法完成。代码示例如下:
1 | Path path = Paths.get("nioTest.txt"); |
处理目录
NIO 2.0
新增的 java.nio.file.DirectoryStream<T>
接口可以非常方便地查找目录中的(符合某种规则的)文件,比如说我们要查找 nioTest 目录下的 txt 后缀的文件,代码示例如下:
1 | // 相对路径 |
处理目录树
目录树意味着一个目录里既有文件也有子目录,也可能都没有,也可能有其一。NIO 2.0
可以很方便地遍历一颗目录树,并操作符合条件的文件;这其中关键的一个方法就是 Files
类的 walkFileTree
,其定义如下:
1 | public static Path walkFileTree(Path start, FileVisitor<? super Path> visitor) |
第二个参数 FileVisitor
被称为文件访问器接口,它实现起来非常复杂,要实现 5 个方法,但幸好 JDK 的设计者提供了一个默认的实现类 SimpleFileVisitor
,如果我们只想从目录树中找到 txt 后缀的文件,可以这样做:
1 | // 相对路径 |
通过创建匿名内部类来重写 SimpleFileVisitor
的 visitFile
方法,如果后缀名为 txt 就打印出来。
文件的删除、复制、移动
删除文件
删除一个文件非常简单,代码示例如下:
1 | Files.delete(file); |
使用 Files.delete()
删除文件之前最好使用 Files.exists()
判断文件是否存在,否则会抛出 NoSuchFileException
;Files.deleteIfExists()
则不用。
复制文件
复制文件也不复杂,代码示例如下:
1 | Path source = Paths.get("nioTest.txt"); |
移动文件
移动文件和复制文件非常相似,代码示例如下:
1 | Path source = Paths.get("nioTest.txt"); |
快速读写文件
NIO 2.0
提供了带有缓冲区的读写辅助方法,通过 Files.newBufferedWriter()
获取一个文件缓冲输入流,并通过 write()
方法写入数据;然后通过 Files.newBufferedReader()
获取一个文件缓冲输出流,通过 readLine()
方法读出数据。代码示例如下。
1 | Path file = Paths.get("nioPathTest.txt"); |
异步I/O操作
异步 I/O 操作可以充分利用多核 CPU 的特点,不需要再像以前那样启动一个线程来对 I/O 进行处理,免得阻塞了主线程的其他操作。
异步 I/O 操作的核心概念是发起非阻塞方式的 I/O 操作,当 I/O 操作完成时通知。可以分为两种形式:Future
和 Callback
。
如果希望主线程发起 I/O 操作并轮循等待结果时,一般使用 Future 的形式;而 Callback 的基本思想是主线程派出一个侦查员(CompletionHandler)到独立的线程中执行 I/O 操作,操作完成后,会触发侦查员的 completed 或者 failed 方法。
Future
示例代码如下:
1 | public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { |
Callback
示例代码如下:
1 | public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { |
不管是
Future
形式还是Callback
形式,总之异步 I/O 是一个强大的特性,可以保证在处理大文件时性能不受到显著的影响。