Java开发笔记(九十二)文件通道的基本用法
前面介绍的各色流式IO在功能方面着实强大,处理文件的时候该具备的操作应有尽有,可流式IO在性能方面不尽如人意,它的设计原理使得实际运行效率偏低,为此从Java4开始增加了NIO技术,通过全新的架构体系带来了可观的性能提升。
NIO是“Non-blocking IO”的缩写,意思是非阻塞的IO,与之相对应,传统的流式IO又被称作BIO(“Blocking IO”的缩写),意即阻塞的IO。所谓阻塞与非阻塞,说起来挺拗口,令人不知所云,这都是设计师脑袋短路惹的祸,发明了这么难懂的词汇,害得初学者一脸懵逼。其实阻塞与非阻塞的区别,犹如私家车与出租车的区别,私家车买回来以后只供车主一家开,没开的时候要么停在小区地库,要么停在公共停车场,其他人是不能随便坐上这部私家车的,如此一来私家车便处于阻塞模式,车门塞住了外人打不开。而出租车整日在街上穿行,有客人招手就停下来载客,开到目的地乘客下车,然后恢复空车状态重新揽客,这样出租车便处于非阻塞模式,车门没塞住乘客打得开。显然阻塞模式存在资源的极大浪费,一个资源分配给某人之后,即使无事可做也只能空在一边闲得发慌;而非阻塞模式充分发挥了物尽其用的原则,一个资源用完之后马上释放,随时允许下一个人接着使用。
非阻塞的NIO机制画了一个高效的大饼,谁知对于文件来说却是画饼充饥,原来非阻塞模式只适用于网络请求交互,而文件处理总是处于阻塞模式,想想看,某个文件被A用户打开之后,B用户还能往该文件写入数据吗?很明显即使A用户打开文件后啥事都不做,B用户也不能写入该文件,缘于文件已经被A用户霸占了。之所以文件没有非阻塞模式,是因为文件仅仅为磁盘上的某个存储片段,它既不智能也不主动,更无法进行任务调度,只能被动的打开和关闭。既然文件处理不支持非阻塞机制,难道NIO技术对文件来说形同虚设?当然事实并非如此,要知道NIO技术不光光包括非阻塞机制,还包括文件通道、虚拟内存等等手段,可谓博大精深、不一而足。
先看文件通道,众所周知,传统的流式IO分为输入流与输出流,输入与输出拥有各自的Stream工具,输入流工具InputStream只能用来读文件,输出流工具OutputStream只能用来写文件,二者井水不犯河水。那如果打开文件之后,想要一会儿读一会儿写,输入流和输出流可得忙坏了,读的时候招呼InputStream来个全套操作,写的时候再招呼OutputStream来个全套操作,实在是劳民伤财。文件通道就不一样,通道中的数据允许双向流动,流进来意味着读操作,流出去意味着写操作,这样文件的读写操作集中在文件通道里进行,大大节省了系统的资源开销。在操作系统层面,通道是一种专职I/O操作的简单处理器,它专门负责输入输出控制,使得CPU从繁琐的I/O处理中解放出来,从而有效地提高整个系统的资源利用率。
文件通道对应的Java类型名叫FileChannel,它的创建方式主要有两种,第一种要通过输入输出流,即调用输入输出流的getChannel方法获取通道对象。比如下面代码根据文件输入流得到了可读的文件通道:
// 第一种方式:根据文件输入流获得可读的文件通道
FileChannel channel1 = new FileInputStream(mFileName).getChannel();
又如下面代码根据文件输出流得到了可写的文件通道:
// 第一种方式:根据文件输出流获得可写的文件通道
FileChannel channel2 = new FileOutputStream(mFileName).getChannel();
第二种方式则要通过随机文件工具,仍旧调用随机文件工具的getChannel方法获取通道对象。此时文件通道对象的构建代码示例如下:
// 第二种方式:根据随机访问文件获得可读的文件通道
FileChannel channel1 = new RandomAccessFile(mFileName, "r").getChannel();
// 第二种方式:根据随机访问文件获得可写的文件通道
FileChannel channel2 = new RandomAccessFile(mFileName,"rw").getChannel();
得到文件通道对象之后,接着便能调用下列方法完成相应的文件处理动作:
isOpen:判断文件通道是否打开。
size:获取文件通道的大小(即文件长度)。
truncate:截断文件大小到指定长度。
read:把文件通道中的数据读到字节缓存。
write:往文件通道写入字节缓存中的数据。
force:强制写入磁盘,相当于缓存输出流的flush方法。
close:关闭文件通道。
从以上方法列表可知,FileChannel相当于集成了FileInputStream和FileOutputStream,用起来更加方便。下面来个利用文件通道写文件的代码例子,一样的简洁明了:
// 通过文件通道写入文件
private static void writeChannel() {
String str = "春眠不觉晓,处处闻啼鸟。\n夜来风雨声,花落知多少。";
// 根据文件输出流获得可写的文件通道。注意文件通道支持try(...)的自动关闭操作
try (FileChannel channel = new FileOutputStream(mFileName).getChannel()) {
// 生成字符串对应的字节缓存对象
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
channel.write(buffer); // 往文件通道写入字节缓存
//channel.force(true); // 强制写入磁盘,相当于输出流的flush方法
} catch (Exception e) {
e.printStackTrace();
}
}
再来利用文件通道读文件的代码例子,具体如下所示:
// 通过文件通道读取文件
private static void readChannel() {
// 根据文件输入流获得可读的文件通道。注意文件通道支持try(...)的自动关闭操作
try (FileChannel channel = new FileInputStream(mFileName).getChannel()) {
int size = (int) channel.size(); // 获取文件通道的大小(即文件长度)
// 分配指定大小的字节缓存
ByteBuffer buffer = ByteBuffer.allocateDirect(size);
channel.read(buffer); // 把文件通道中的数据读到字节缓存
buffer.flip(); // 把缓冲区从写模式切换到读模式。从缓冲区读取数据之前,必须先调用flip方法
byte[] bytes = new byte[size]; // 创建与文件大小相同长度的字节数组
buffer.get(bytes); // 把字节缓存中的数据取到字节数组
String content = new String(bytes); // 把字节数组转换为字符串
System.out.println("content="+content);
} catch (Exception e) {
e.printStackTrace();
}
}
看来文件通道在读文件过程中也使用了缓存,整体的代码流程同缓存输入流BufferedInputStream类似,只不过与FileChannel搭配的字节缓存ByteBuffer用着不太顺手,别着急,后面的文章将细细道来ByteBuffer的详细用法。
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(九十二)文件通道的基本用法的更多相关文章
- Java开发笔记(二十四)方法的组成形式
经过前面的学习,我们发现演示的Java代码越来越复杂,而且每个例子的代码都堆在入口方法main内部,这会导致如下问题:1.一个方法内部堆砌了太多的代码行,看着费神,维护起来也吃力:2.部分代码描述的是 ...
- Java开发笔记(二十三)数组工具Arrays
数组作为一种组合形式的数据类型,必然要求提供一些处理数组的简便办法,包括数组比较.数组复制.数组排序等等.为此Java专门设计了Arrays工具,该工具包含了几个常用方法,方便程序员对数组进行加工操作 ...
- Java开发笔记(二十五)方法的输入参数
前面通过main方法介绍了方法的定义形式,对于方法的输入参数来说,还有几个值得注意的地方,接下来分别对输入参数的几种用法进行阐述.一个方法可以有输入参数,也可以没有输入参数,倘若无需输入参数,则方法定 ...
- Java开发笔记(二十六)方法的输出参数
前面介绍了方法的输入参数,与输入参数相对应的则为输出参数,输出参数也被称作方法的返回值,意思是经过方法的处理最终得到的运算数值.这个返回值可能是整型数,也可能是双精度数,也可能是数组等其它类型,甚至允 ...
- Java开发笔记(二十七)数值包装类型
方法的出现缘起优化代码结构,但它的意义并不局限于此,正因为有了方法定义,编程语言才更像一门能解决实际问题的工具,而不仅仅是只能用于加减乘除的计算器.在数学的发展过程中,为了表示四则运算,人们创造了加减 ...
- Java开发笔记(二十八)布尔包装类型
前面介绍了数值包装类型,因为不管是整数还是小数,它们的运算操作都是类似的,所以只要学会了Integer的用法,其它数值包装类型即可一并掌握.但是对于布尔类型boolean来说,该类型定义的是“true ...
- Java开发笔记(二十九)大整数BigInteger
早期的编程语言为了节约计算机的内存,给数字变量定义了各种存储规格的数值类型,比如字节型byte只占用一个字节大小,短整型short占用两个字节大小,整型int占用四个字节大小,长整型long占用八个字 ...
- Java开发笔记(二)Java工程的帝国区划
上一篇文章介绍了如何运行了第一个Java程序“Hello World”.然而这个开发环境看起来那么陌生,一个个名字符号完全不知道它们是干啥的呀,对于初学者来说,好比天书一般,多看几眼感觉都要走火入魔了 ...
- Java开发笔记(二十二)神奇的冒号
Java中的标点符号主要有两类用途,一类是运算符,包括加号+.减号-.乘号*.除号/.取余号%.等号=.大于号>.小于号<.与号&.或号|.非号!.异或号^等等,另一类则是分隔符, ...
- Java开发笔记(二十)一维数组的用法
之前介绍的各类变量都是单独声明的,倘若要求定义相同类型的一组变量,则需定义许多同类型的变量,显然耗时耗力且不宜维护.为此,编程语言引入了数组的概念,每个数组都由一组相同类型的数据构成,对外有统一的数组 ...
随机推荐
- CentOS7下安装单机版RabbitMQ及权限赋予
RabbitMQ官网rpm软件包地址:https://www.rabbitmq.com/releases/ 一.安装环境: CentOS7.erlang-19.0.4-1.el7.centos.x86 ...
- GitHub现VMware虚拟机逃逸EXP,利用三月曝光的CVE-2017-4901漏洞
今年的Pwn2Own大赛后,VMware近期针对其ESXi.Wordstation和Fusion部分产品发布更新,修复在黑客大赛中揭露的一些高危漏洞.事实上在大赛开始之前VMware就紧急修复了一个编 ...
- samba服務器下文件夾chmod權限技巧
需要的效果: samba下文件夹(abc)不可以被重命名.不可以被刪除,其所有子目录可读可写. 如何做到: chmod 777 -R abc # -R 使得abc下所有数据继承可读可写权限 chm ...
- PC_excel完毕一列英文小写变大写
原创作品,出自 "深蓝的blog" 博客.欢迎转载,转载时请务必注明出处.否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong ...
- Linux系统下ssh登陆很慢的解决办法
很多的Linux用户发现连接上Linux服务器在输入用户名之后还要再等一下才能输入密码,时间过长了,现在小白与大家分享一下如何解决ssh登陆问题的问题,希望对您有所帮助. 1.我们平时登陆Linux服 ...
- jQuery -> 获取指定上下文中的DOM元素
jQuery函数的第二个參数能够指定DOM元素的搜索范围. 第二个參数可分为下面类型 DOM reference jQuery wrapper document 代码演示样例 <!DOCTYPE ...
- Tomcat 隐藏Server Name
隐藏Http请求中的Header ServerName 方法一 在tomcat/lib/tomcat-coyote.jar中 下面两个文件 org/apache/coyote/http11/Const ...
- VMWare中的Host-only、NAT、Bridge的比較
VMWare有Host-only(主机模式).NAT(网络地址转换模式)和Bridged(桥接模式)三种工作模式. 1.bridged(桥接模式) 在这样的模式下.VMWare虚拟出来的操作系统就像是 ...
- URL 字段简析
URL:统一资源定位符:URL是uri的一个子集,另外一个子集是URN. URL语法:(来自HTTP权威指南中文版P29) 组件 描述 默认值 方案 访问服务器以获取资源时要使用哪种协议 无 用户 某 ...
- 说说循环与闭包——《你不知道的JS》读书笔记(一)
什么是闭包 <你不知道的JS>里有对闭包的定义:"当函数可以记住并访问所在的词法作用域,即使函数是在当前作用域之外执行,这就产生了闭包." 讲闭包是啥的太多了...就一 ...