Java:前程似锦的 NIO 2.0

原作者:沉默王二


Java 之所以能够霸占编程语言的榜首,其强大、丰富的类库功不可没,几乎所有的编程问题都能在其中找到解决方案。但在早期的版本当中,输入输出(I/O)流并不那么令开发者感到愉快:

  • 1)JDK 1.4 之前的 I/O 没有缓冲区的概念、不支持正则表达式、支持的字符集编码有限等等;
  • 2)JDK 1.4 的时候引入了非阻塞 I/O,也就是 NIO 1.0,但遍历目录很困难,不支持文件系统的非阻塞操作等等。

为了突破这些限制,JDK 1.7 的时候引入了新的 NIO,也就是本篇文章的主角——NIO 2.0。

01、基石:Path

Path 既可以表示一个目录,也可以表示一个文件,就像 File 那样——当然了,Path 就是用来取代 File 的。

  • 1)可以通过 Paths.get() 创建一个 Path 对象,此时 Path 并没有真正在物理磁盘上创建;参数既可以是一个文件名,也可以是一个目录名;绝对路径或者相对路径均可。
  • 2)可以通过 Files.notExists() 确认 Path(目录或者文件) 是否已经存在。
  • 3)可以通过 Files.createDirectory() 创建目录,此时目录已经在物理磁盘上创建成功,可通过资源管理器查看到。
  • 4)可以通过 Files.createFile() 创建文件,此时文件已经在物理磁盘上创建成功,可通过资源管理器查看到。
  • 5)可以通过 toAbsolutePath() 查看 Path 的绝对路径。
  • 6)可以通过 resolve() 将 Path 连接起来,参数可以是一个新的 Path 对象,也可以是对应的字符串。

具体的代码如下:

public class Wanger {public static void main(String[] args) {// 相对路径Path dir = Paths.get("chenmo");// 输出 dir 的绝对路径System.out.println(dir.toAbsolutePath()); // 输出:D:\program\java.git\java_demo\chenmoif (Files.notExists(dir)) {try {// 如果目录不存在,则创建目录Files.createDirectory(dir);} catch (IOException e1) {e1.printStackTrace();}}// 这时候 chenmo.txt 文件并未创建// 通过 resolve 方法把 dir 和 chenmo.txt 链接起来Path file = dir.resolve("chenmo.txt");// 输出 file 的绝对路径System.out.println(file.toAbsolutePath()); // 输出:D:\program\java.git\java_demo\chenmo\chenmo.txtif (Files.notExists(file)) {try {// 如果文件不存在,则创建文件Files.createFile(file);} catch (IOException e) {e.printStackTrace();}}}}

如果要将 File 转换为 Path,可以通过 File 类的 toPath() 方法完成。代码示例如下:

File file = new File("沉默王二.txt");
Path path = file.toPath();

如果要将 Path 转换为 File,可以通过 Path 类的 toFile() 方法完成。代码示例如下:

Path path = Paths.get("沉默王二.txt");
File file = path.toFile();

02、处理目录

NIO 2.0 新增的 java.nio.file.DirectoryStream<T> 接口可以非常方便地查找目录中的(符合某种规则的)文件,比如说我们要查找 chenmo 目录下的 txt 后缀的文件,代码示例如下:

// 相对路径
Path dir = Paths.get("chenmo");try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt")) {for (Path entry : stream) {System.out.println(entry.getFileName());}
} catch (IOException e) {e.printStackTrace();
}
  • 1)Files.newDirectoryStream(Path dir, String glob) 会返回一个过滤后的 DirectoryStream( 目录流,),第一个参数为目录,第二个参数为 glob 表达式,比如 *.txt 表示所有 txt 后缀的文件。
  • 2)由于 DirectoryStream 继承了 Closeable 接口,所以它可以配合 try-with-resources 语法写出更安全的代码,目录流会自动调用 close 方法关闭流,释放与流相关的资源,不需要再通过 finally 进行主动关闭。
  • 3)DirectoryStream 被称为目录流,允许方便地使用 for-each 结构来遍历目录。

03、处理目录树

目录树意味着一个目录里既有文件也有子目录,也可能都没有,也可能有其一。NIO 2.0 可以很方便地遍历一颗目录树,并操作符合条件的文件;这其中关键的一个方法就是 Files 类的 walkFileTree,其定义如下:

public static Path walkFileTree(Path start, FileVisitor<? super Path> visitor)throws IOException {return walkFileTree(start,EnumSet.noneOf(FileVisitOption.class),Integer.MAX_VALUE,visitor);
}

第二个参数 FileVisitor 被称为文件访问器接口,它实现起来非常复杂,要实现 5 个方法呢,但幸好 JDK 的设计者提供了一个默认的实现类 SimpleFileVisitor,如果我们只想从目录树中找到 txt 后缀的文件,可以这样做:

// 相对路径
Path dir = Paths.get("chenmo");try {Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {if (file.toString().endsWith(".txt")) {System.out.println(file.getFileName());}return FileVisitResult.CONTINUE;}});
} catch (IOException e) {e.printStackTrace();
}

通过创建匿名内部类来重写 SimpleFileVisitor 的 visitFile 方法,如果后缀名为 txt 就打印出来。

04、文件的删除、复制、移动

创建一个文件非常的简单,之前我们已经体验过了,那么删除一个文件也同样的简单,代码示例如下:

Files.delete(file);
Files.deleteIfExists(file);

使用 Files.delete() 删除文件之前最好使用 Files.exists() 判断文件是否存在,否则会抛出 NoSuchFileException;Files.deleteIfExists() 则不用。

复制文件也不复杂,代码示例如下:

Path source = Paths.get("沉默王二.txt");
Path target = Paths.get("沉默王二1.txt");
Files.copy(source, target);

移动文件和复制文件非常相似,代码示例如下:

Path source = Paths.get("沉默王二.txt");
Path target = Paths.get("沉默王二1.txt");
Files.move(source, target);

05、快速地读写文件

NIO 2.0 提供了带有缓冲区的读写辅助方法,使用起来也非常的简单。可以通过 Files.newBufferedWriter() 获取一个文件缓冲输入流,并通过 write() 方法写入数据;然后通过 Files.newBufferedReader() 获取一个文件缓冲输出流,通过 readLine() 方法读出数据。代码示例如下。

Path file = Paths.get("沉默王二.txt");try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {writer.write("一个有趣的程序员");
} catch (Exception e) {e.printStackTrace();
}try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}
} catch (Exception e) {e.printStackTrace();
}

06、重要:异步 I/O 操作

实话实说吧,上面提到的那些都算是 NIO 2.0 的甜点,而异步 I/O 操作(也称 AIO)才算是真正重要的内容。异步 I/O 操作可以充分利用多核 CPU 的特点,不需要再像以前那样启动一个线程来对 I/O 进行处理,免得阻塞了主线程的其他操作。

异步 I/O 操作的核心概念是发起非阻塞方式的 I/O 操作,当 I/O 操作完成时通知。可以分为两种形式:Future 和 Callback。如果我们希望主线程发起 I/O 操作并轮循等待结果时,一般使用 Future 的形式;而 Callback 的基本思想是主线程派出一个侦查员(CompletionHandler)到独立的线程中执行 I/O 操作,操作完成后,会触发侦查员的 completed 或者 failed 方法。

1)Future

先来看一个示例,代码如下:

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {Path file = Paths.get("沉默王二.txt");AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);Future<Integer> result = channel.read(ByteBuffer.allocate(100_000), 0);while (!result.isDone()) {System.out.println("主线程继续做事情");}Integer bytesRead = result.get();System.out.println(bytesRead);
}
  • 1)通过 AsynchronousFileChannel.open() 打开一个异步文件通道 channel。
  • 2)用 Future 来保存从通道中读取的结果。
  • 3)通过 isDone() 轮循判断异步 I/O 操作是否完成,如果没有完成的话,主线程可以继续做自己的事情。

2)Callback

先来看一个示例,代码如下:

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {Path file = Paths.get("沉默王二.txt");AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);channel.read(ByteBuffer.allocate(100_000), 0, null, new CompletionHandler<Integer, ByteBuffer>() {public void completed(Integer result, ByteBuffer attachment) {System.out.println(result);}public void failed(Throwable exc, ByteBuffer attachment) {System.out.println(exc.getMessage());}});System.out.println("主线程继续做事情");}
  • 1)通过 AsynchronousFileChannel.open() 打开一个异步文件通道 channel。
  • 2)在 read 方法中使用匿名内部类的形式启用 CompletionHandler,然后实现 CompletionHandler 的两个监听方法,completed 的时候打印结果,failed 的时候打印异常信息。

不管是 Future 形式还是 Callback 形式,总之异步 I/O 是一个强大的特性,可以保证在处理大文件时性能不受到显著的影响。


 
转载来源:https://blog.csdn.net/qing_gee/article/details/96989050 

Java:前程似锦的 NIO 2.0相关推荐

  1. java 套接字关联的通道_Java 通道教程 – NIO 2.0

    # Java 通道教程 – NIO 2.0 > 原文: [https://howtodoinjava.com/java7/nio/java-nio-2-0-channels/](https:// ...

  2. Java BIO、NIO、AIO

    同步与异步 同步与异步的概念, 关注的是 消息通信机制 同步是指发出一个请求, 在没有得到结果之前该请求就不返回结果, 请求返回时, 也就得到结果了. 比如洗衣服, 把衣服放在洗衣机里, 没有洗好之前 ...

  3. 【学习笔记】JAVA IO与NIO(new IO)的对比与不同IO模型的理解

    JAVA IO 分类: 几种IO 模型 1. 阻塞 IO 模型 2. 非阻塞 IO 模型 JAVA NIO 多路复用 IO 模型(即Java中的NIO) JAVA IO 思维导图: 分类: 按照流的方 ...

  4. java 修改最大nio连接数_关于java流的几个概念:IO、BIO、NIO、AIO,有几个人全知道?...

    关于同步.阻塞的知识我之前的文章有介绍,所以关于流用到这些概念与之前多线程用的概念一样. 下面具体来看看java中的几种流 IO/BIO BIO就是指IO,即传统的Blocking IO,即同步并阻塞 ...

  5. java 修改最大nio连接数_携程基于Quasar协程的NIO实践

    IO密集型系统在高并发场景下,会有大量线程处于阻塞状态,性能低下,JAVA上成熟的非阻塞IO(NIO)技术可解决该问题.目前Java项目对接NIO的方式主要依靠回调,代码复杂度高,降低了代码可读性与可 ...

  6. java中的NIO和IO到底是什么区别?20个问题告诉你答案

    摘要:NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多. 本文分享自华为云社区<jav ...

  7. java最大文件描述符,java – 为什么JDK NIO使用这么多的anon_inode文件描述符?

    我正在使用Sun的JDK 1.6.0_26和NIO(使用Netty),在lsof中我看到数百个文件描述符是anon_inode: $lsof -np 11225 | fgrep -w anon_ino ...

  8. Java IO BIO NIO

    Java IO BIO NIO 一.Java I/O概述 1.1 什么是流 1.2 流的分类 1.3 字符流 1.3.1 Reader 1.3.2 Writer 1.4 字节流 1.4.1 Input ...

  9. JAVA IO : BIO NIO AIO

    JAVA IO : BIO NIO AIO 同步异步.阻塞非阻塞概念 同步与异步 阻塞与非阻塞 IO VS NIO VS AIO 面向流与面向缓冲 阻塞与非阻塞IO BIO.NIO.AIO的JAVA实 ...

最新文章

  1. iOS架构-cocoaPods之自制私有库及管理(17)
  2. coolite TreePanel CheckBox联动
  3. shell 与 空格
  4. collect的功能是什么?其底层如何实现的?_为什么你要用 Spring ?
  5. AI读懂两千年前文字,登上Nature封面,惊艳历史学家
  6. 【英语学习】【English L06】U07 Jobs L2 I have my own bakery now
  7. 《Java8实战》-第五章读书笔记(使用流Stream-02)
  8. 人工智能史(来自wiki)
  9. 6603网狐棋牌搭建视频教程
  10. 【100%通过率】华为OD机试真题 C++ 实现【完美走位】【2022.11 Q4新题】
  11. 学python需要什么英语水平_学python需要英语基础吗
  12. 深入剖析Tomcat第一章ERR_INVALID_HTTP_RESPONSE
  13. SQL取日期时间部分
  14. 49个项目管理过程ITTO整理(详细)
  15. 深入理解JVM(高级进阶)
  16. SpringCloud Gateway 详解
  17. [BZOJ1787][Ahoi2008]Meet 紧急集合
  18. CAD导入Revit缺少东西原因-Revit中如何批量导出CAD图纸
  19. Vue的生命周期钩子函数介绍
  20. 能量山水画家武湲承:搬运时空能量,聚藏山水画中

热门文章

  1. 学习和使用web标准的十大理由
  2. UIScrollView 滚动视图 (实例)
  3. spring IOC容器 Bean 管理——基于注解方式
  4. 02241107班编程题 切片逆序大小写等
  5. 草稿 断开始datagrewvies 01
  6. 演练 青鸟游戏迷你平台
  7. python-循环-打印菱形图案
  8. botstrap-栅格布局与栅格偏移
  9. elasticsearch 去重计数
  10. 输入网址后到网页显示出来会发生什么?