java输入输出 -- java NIO之文件通道
一、简介
通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作。与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动。通道既可以读,也可以写。
Java NIO 出现在 JDK 1.4 中,由于 NIO 效率高于传统的 IO,所以 Sun 公司从底层对传统 IO 的实现进行了修改。修改的方式就是在保证兼容性的情况下,使用 NIO 重构 IO 的方法实现,无形中提高了传统 IO 的效率。
所有的 NIO 操作始于通道,通道是数据来源或数据写入的目的地。
从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。
从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据
二、基本操作
通道类型分为两种。一种是文件通道(面向文件的),另一种是Socket通道(面向网络的)。具体的类声明如下:
FileChannel:文件通道,用于文件的读和写
DatagramChannel:用于 UDP 连接的接收和发送
SocketChannel:把它理解为 TCP 连接通道,简单理解就是 TCP 客户端
ServerSocketChannel:TCP 对应的服务端,用于监听某个端口进来的请求
Channel 经常翻译为通道,类似 IO 中的流,用于读取和写入。它与前面介绍的 Buffer 打交道,读操作的时候将 Channel 中的数据填充到 Buffer 中,而写操作时将 Buffer 中的数据写入到 Channel 中。
三、通道
(一).FileChannel文件通道
FileChannel 是一个用于连接文件的通道,通过该通道,既可以从文件中读取,也可以向文件中写入数据。与SocketChannel 不同,FileChannel 无法设置为非阻塞模式,这意味着它只能运行在阻塞模式下。
在使用FileChannel 之前,需要先打开它。由于 FileChannel 是一个抽象类,所以不能通过直接创建而来。必须通过像 InputStream、OutputStream 或 RandomAccessFile 等实例获取一个 FileChannel 实例。
FileInputStream fis = new FileInputStream(FILE_PATH);
FileChannel channel = fis.getChannel(); FileOutputStream fos = new FileOutputStream(FILE_PATH);
FileChannel channel = fis.getChannel(); RandomAccessFile raf = new RandomAccessFile(FILE_PATH , "rw");
FileChannel channel = raf.getChannel();
读写操作
// 获取管道
RandomAccessFile raf = new RandomAccessFile(FILE_PATH, "rw");
FileChannel rafChannel = raf.getChannel(); // 准备数据
String data = "新数据,时间: " + System.currentTimeMillis();
System.out.println("原数据:\n" + " " + data);
//准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(128);
buffer.clear();
buffer.put(data.getBytes());
buffer.flip(); // 向管道中写入数据
rafChannel.write(buffer); rafChannel.close();
raf.close();//管道关闭 // 重新打开管道
raf = new RandomAccessFile(FILE_PATH, "rw");
rafChannel = raf.getChannel(); // 读取管道中的数据
buffer.clear();
rafChannel.read(buffer); // 打印读取出的数据
buffer.flip();
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
System.out.println("读取到的数据:\n" + " " + new String(bytes)); rafChannel.close();
raf.close();
//结果如下
数据转移操作
我们有时需要将一个文件中的内容复制到另一个文件中去,最容易想到的做法是利用传统的 IO 将源文件中的内容读取到内存中,然后再往目标文件中写入。
现在,有了 NIO,我们可以利用更方便快捷的方式去完成复制操作。FileChannel 提供了一对数据转移方法 - transferFrom/transferTo,通过使用这两个方法,即可简化文件复制操作。
public static void main(String[] args) throws IOException {
RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel(); long position = 0;
long count = fromChannel.size(); // 将 fromFile 文件找那个的数据转移到 toFile 中去
System.out.println("before transfer: " + readChannel(toChannel));
fromChannel.transferTo(position, count, toChannel);
System.out.println("after transfer : " + readChannel(toChannel)); fromChannel.close();
fromFile.close();
toChannel.close();
toFile.close();
} private static String readChannel(FileChannel channel) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(32);
buffer.clear(); // 将 channel 读取位置设为 0,也就是文件开始位置
channel.position(0);
channel.read(buffer); // 再次将文件位置归零
channel.position(0); buffer.flip();
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
return new String(bytes);
}
//结果
通过上面的代码,我们可以明显感受到,利用 transferTo 减少了编码量。那么为什么利用 transferTo 可以减少编码量呢?在解答这个问题前,先来说说程序读取数据和写入文件的过程。
我们现在所使用的 PC 操作系统,将内存分为了内核空间和用户空间。操作系统的内核和一些硬件的驱动程序就是运行在内核空间内,而用户空间就是我们自己写的程序所能运行的内存区域。
这里,当我们调用 read 从磁盘中读取数据时,内核会首先将数据读取到内核空间中,然后再将数据从内核空间复制到用户空间内。也就是说,我们需要通过内核进行数据中转。同样,写入数据也是如此。系统先从用户空间将数据拷贝到内核空间中,然后再由内核空间向磁盘写入。相关示意图如下:
与上面的数据流向不同,FileChannel 的 transferTo 方法底层基于 sendfile64(Linux 平台下)系统调用实现。sendfile64 会直接在内核空间内进行数据拷贝,免去了内核往用户空间拷贝,用户空间再往内核空间拷贝这两步操作,因此提高了效率。其示意图如下:
通过上面的讲解,大家应该知道了 transferTo 和 transferFrom 的效率会高于传统的 read 和 write 在效率上的区别。区别的原因在于免去了内核空间和用户空间的相互拷贝,虽然内存间拷贝的速度比较快,但涉及到大量的数据拷贝时,相互拷贝的带来的消耗是不应该被忽略的。
其他操作
FileChannel 还有一些其他的方法,这里通过一个表格来列举这些方法,就不一一展开说明了。如下:
方法名 | 用途 |
---|---|
position | 返回或修改通道读写位置 |
size | 获取通道所关联文件的大小 |
truncate | 截断通道所关联的文件 |
force | 强制将通道中的新数据刷新到文件中 |
close | 关闭通道 |
lock | 对通道文件进行加锁 |
四、总结
以上章节对 NIO 文件通道的用法和部分方法的实现进行了简单分析。
从上面的分析可以看出,NIO FileChannel 在实现上,实际上是对底层操作系统的一些 API 进行了再次封装,也就是一层皮。有了这层封装后,对上就屏蔽了底层 API 的细节,以降低使用难度。
Java 为了提高开发效率,屏蔽了操作系统层面的细节。虽然 Java 可以屏蔽这些细节,但作为开发人员,我觉得我们不能也去屏蔽这些细节(虽然不了解这些细节也能写代码),有时间还是应该多了解了解这些底层的东西。毕竟要想往更高的层次发展,这些底层的知识必不可少。
java输入输出 -- java NIO之文件通道的更多相关文章
- JAVA NIO之文件通道
1.简介 通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作.与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动.通道既可以读, ...
- java输入输出 -- Java NIO之套接字通道
一.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 – 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为Be ...
- java输入输出 -- Java NIO之选择器
一.简介 前面的文章说了缓冲区,说了通道,本文就来说说 NIO 中另一个重要的实现,即选择器 Selector.在更早的文章中,我简述了几种 IO 模型.如果大家看过之前的文章,并动手写过代码的话.再 ...
- java输入输出 -- java NIO之缓存区Buffer
一.简介 java NIO相关类在jdk1.4被引入,用于提高I/O的效率.java NIO包含很多东西,但核心的东西不外乎Buffer.channel和selector.本文先来看Buffer的实现 ...
- java 输入输出IO流 RandomAccessFile文件的任意文件指针位置地方来读写数据
RandomAccessFile的介绍: RandomAccessFile是Java输入输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出 ...
- Java NIO2:NIO概述
一.概述 从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新特性,被统称为NIO(即New I/O).新增了许多用于处理输入输出的类,这些类都被放在java.nio包及子包下,并且对原j ...
- Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(四)----文件通道和网络通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 文件通道 FileChannel 用法
FileChannel 提供了一种通过通道来访问文件的方式,它可以通过带参数 position(int) 方法定位到文件的任意位置开始进行操作,还能够将文件映射到直接内存,提高大文件的访问效率.本文将 ...
随机推荐
- JavaBean转Map
1.需要的jar包 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava ...
- Tkinter 之Grid布局
一.参数说明 参数 作用 column 指定组件插入的列(0 表示第 1 列)默认值是 0 columnspan 指定用多少列(跨列)显示该组件 row 指定组件插入的行(0 表示第 1 行) ...
- BZOJ4237稻草人——单调栈+CDQ分治
题目描述 JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典. 有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地.和启示中的一样,田地需要满足以下条件: ...
- 【原】Python基础-循环语句
x = 1while x <= 10: print(x) x += 1 password = ""while password != "3213554": ...
- 编程微语 2019-Autumn
很多时候我们要的是[网页全屏],可是许多软件却做成了[浏览器全屏],不要一听到[全屏]就认为真的是传统意义上的全屏.拜托,老板(往往就是最大的产品经理).产品经理.程序员,想想,说清楚,做正确.某度文 ...
- [RK3399] /bin/sh: 1: lz4c: not found
CPU:RK3399 系统:Android 8.1 第一次在 RK3399 编译 Android 8.1 的系统,编译内核过程中报错如下: /bin/sh: : lz4c: not found mak ...
- windows 家庭版 开启Hyper-V
windows 家庭版无法开启Hyper-V ,建议升级成专业版.
- jQuery 设置select,radio的值,无法自动触发绑定的change事件
一.问题 今天在对select和radio做change事件绑定后,手动设置其value值,但是不能触发change事件 二.解决 使用trigger方法手动触发
- sql查询表的所有字段和表字段对应的类型
1.查询表的所有字段 select syscolumns.name from syscolumns where id=object_id('写上要查询的表名') 2.查询表的所有字段+表字段对应的类型 ...
- Linux中显示系统中USB信息的lsusb命令
来源:Linux中国 原文:https://linux.cn/article-2448-1.html 通用串行总线(USB)被设计成为连接计算机外设的标准,如键盘.鼠标.打印机.数码相机.便携式媒体 ...