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\chenmo
if (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.txt
if (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>() {
@Override
public 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 是一个强大的特性,可以保证在处理大文件时性能不受到显著的影响。
Java:前程似锦的 NIO 2.0的更多相关文章
- JAVA的 IO NIO AIO笔记
IO linux内核将所有外部设备都看做一个文件来操作,对一个文件的读写会调用内核系统命令,放回一个file descriptor(文件描述符), 对一个socket的读写也会有相应 ...
- Java中的NIO基础知识
上一篇介绍了五种NIO模型,本篇将介绍Java中的NIO类库,为学习netty做好铺垫 Java NIO 由3个核心组成,分别是Channels,Buffers,Selectors.本文主要介绍着三个 ...
- Java的BIO,NIO和AIO的区别于演进
作者:公众号:我是攻城师 前言 Java里面的IO模型种类较多,主要包括BIO,NIO和AIO,每个IO模型都有不一样的地方,那么这些IO模型是如何演变呢,底层的原理又是怎样的呢? 本文我们就来聊聊. ...
- Java BIO、NIO、AIO
同步与异步 同步与异步的概念, 关注的是 消息通信机制 同步是指发出一个请求, 在没有得到结果之前该请求就不返回结果, 请求返回时, 也就得到结果了. 比如洗衣服, 把衣服放在洗衣机里, 没有洗好之前 ...
- JAVA中的NIO (New IO)
简介 标准的IO是基于字节流和字符流进行操作的,而JAVA中的NIO是基于Channel和Buffer进行操作的. 传统IO graph TB; 字节流 --> InputStream; 字节流 ...
- Java BIO、NIO与AIO的介绍(学习过程)
Java BIO.NIO与AIO的介绍 因为netty是一个NIO的框架,所以在学习netty的过程中,开始之前.针对于BIO,NIO,AIO进行一个完整的学习. 学习资源分享: Netty学习:ht ...
- 如何解读 Java IO、NIO 中的同步阻塞与同步非阻塞?
原文链接:如何解读 Java IO.NIO 中的同步阻塞与同步非阻塞? 一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写 ...
- java中的NIO和IO到底是什么区别?20个问题告诉你答案
摘要:NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多. 本文分享自华为云社区<jav ...
- android异常: java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused
android手机做下载文件时,报了如下异常: java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused 模拟器 ...
随机推荐
- 使用Netty实现通用二进制协议的高效数据传输
Netty是一个高性能的NIO通信框架,提供异步的.事件驱动的网络编程模型.使用Netty可以方便用户开发各种常用协议的网络程序.例如:TCP.UDP.HTTP等等. Netty的最新版本是3.2.7 ...
- 【转】简单的jQuery插件开发方法
在实际开发工作中,总会碰到像滚动,分页,日历等展示效果的业务需求,对于接触过jQuery以及熟悉jQuery使用的人来说,首先想到的肯定是寻找现有的jQuery插件来满足相应的展示需求.目前页面中常用 ...
- HTTP请求GET和POST的区别
HTTP请求GET和POST的区别: 1.GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头<request-line>中), 以?分割URL和传输数据,多个参数用&a ...
- java方法中Collection集合的基本使用与方法
集合类的由来,对象用于封闭特有数据,对象多了需要存储,如果对象的个数不确定就使用集合容器进行存储. 集合特点:1.用于存储对象的容器.2.集合的长度是可变的.3.集合中不可以存储基本数据类型值. 集合 ...
- 生产环境MySQL优化
a:硬件的优化: 1. 采用64位cpu,cpu至少4颗,L2缓存越大越好2. 内存要大,32-64G运行1-2个实例,96-128G运行3-4个实例3. 机械盘选用sas盘,转速15000以上,有可 ...
- 第二章 在Linux上部署.net core
项目目标部署环境:CentOS 7+ 项目技术点:.netcore2.0 + Autofac +webAPI + NHibernate5.1 + mysql5.6 + nginx 开源地址:https ...
- 高性能高并发网站架构,教你搭建Redis5缓存集群
一.Redis集群介绍 Redis真的是一个优秀的技术,它是一种key-value形式的NoSQL内存数据库,由ANSI C编写,遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Val ...
- python学习之路--python基础之列表操作
本文主要介绍下python列表的的一些基本操作 列表是我们在python编程中常用的数据类型之一,通过列表我们可以对数据实现最方便的存储.修改等操作. 定义列表 names=['ZhangSan',' ...
- Hadoop —— 单机环境搭建
一.前置条件 Hadoop的运行依赖JDK,需要预先安装,安装步骤见: Linux下JDK的安装 二.配置免密登录 Hadoop组件之间需要基于SSH进行通讯. 2.1 配置映射 配置ip地址和主机名 ...
- 另一个ACM之路建议
ACM联系建议 一位高手对我的建议: 一般要做到50行以内的程序不用调试.100行以内的二分钟内调试成功.acm主要是考算法的 ,主要时间是花在思考算法上,不是花在写程序与debug上. 下面给个计划 ...