前面介绍的各色流式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开发笔记(九十二)文件通道的基本用法的更多相关文章

  1. Java开发笔记(二十四)方法的组成形式

    经过前面的学习,我们发现演示的Java代码越来越复杂,而且每个例子的代码都堆在入口方法main内部,这会导致如下问题:1.一个方法内部堆砌了太多的代码行,看着费神,维护起来也吃力:2.部分代码描述的是 ...

  2. Java开发笔记(二十三)数组工具Arrays

    数组作为一种组合形式的数据类型,必然要求提供一些处理数组的简便办法,包括数组比较.数组复制.数组排序等等.为此Java专门设计了Arrays工具,该工具包含了几个常用方法,方便程序员对数组进行加工操作 ...

  3. Java开发笔记(二十五)方法的输入参数

    前面通过main方法介绍了方法的定义形式,对于方法的输入参数来说,还有几个值得注意的地方,接下来分别对输入参数的几种用法进行阐述.一个方法可以有输入参数,也可以没有输入参数,倘若无需输入参数,则方法定 ...

  4. Java开发笔记(二十六)方法的输出参数

    前面介绍了方法的输入参数,与输入参数相对应的则为输出参数,输出参数也被称作方法的返回值,意思是经过方法的处理最终得到的运算数值.这个返回值可能是整型数,也可能是双精度数,也可能是数组等其它类型,甚至允 ...

  5. Java开发笔记(二十七)数值包装类型

    方法的出现缘起优化代码结构,但它的意义并不局限于此,正因为有了方法定义,编程语言才更像一门能解决实际问题的工具,而不仅仅是只能用于加减乘除的计算器.在数学的发展过程中,为了表示四则运算,人们创造了加减 ...

  6. Java开发笔记(二十八)布尔包装类型

    前面介绍了数值包装类型,因为不管是整数还是小数,它们的运算操作都是类似的,所以只要学会了Integer的用法,其它数值包装类型即可一并掌握.但是对于布尔类型boolean来说,该类型定义的是“true ...

  7. Java开发笔记(二十九)大整数BigInteger

    早期的编程语言为了节约计算机的内存,给数字变量定义了各种存储规格的数值类型,比如字节型byte只占用一个字节大小,短整型short占用两个字节大小,整型int占用四个字节大小,长整型long占用八个字 ...

  8. Java开发笔记(二)Java工程的帝国区划

    上一篇文章介绍了如何运行了第一个Java程序“Hello World”.然而这个开发环境看起来那么陌生,一个个名字符号完全不知道它们是干啥的呀,对于初学者来说,好比天书一般,多看几眼感觉都要走火入魔了 ...

  9. Java开发笔记(二十二)神奇的冒号

    Java中的标点符号主要有两类用途,一类是运算符,包括加号+.减号-.乘号*.除号/.取余号%.等号=.大于号>.小于号<.与号&.或号|.非号!.异或号^等等,另一类则是分隔符, ...

  10. Java开发笔记(二十)一维数组的用法

    之前介绍的各类变量都是单独声明的,倘若要求定义相同类型的一组变量,则需定义许多同类型的变量,显然耗时耗力且不宜维护.为此,编程语言引入了数组的概念,每个数组都由一组相同类型的数据构成,对外有统一的数组 ...

随机推荐

  1. JSP处理XML数据

    以下内容引用自http://wiki.jikexueyuan.com/project/jsp/xml-data.html: 当通过HTTP发送XML数据时,使用JSP处理传入和传出的XML文件是有意义 ...

  2. Linux NFS服务器的安装与配置(转载)

    一.NFS服务简介 NFS 是Network File System的缩写,即网络文件系统.一种使用于分散式文件系统的协定,由Sun公司开发,于1984年向外公布.功能是通过网络让不同的机器.不同的操 ...

  3. 基于 Java 的开源网络爬虫框架 WebCollector

    原文:https://www.oschina.net/p/webcollector

  4. jenkinsapi出现HTTPConnectionPool Max retires exceeded异常

    python项目通过使用jenkinsapi远程控制jenkins jenkinsapi使用的远程连接方法是requests包,requests包又使用了urllib3,urllib3又引用了http ...

  5. java STW stop the world 哈哈就是卡住了

    java  STW  stop the world 哈哈就是卡住了 学习了:http://www.jb51.net/article/125400.htm

  6. Growth: 一个关于怎样成为优秀Web Developer 的 App

    想了想还是决定在今天公布一个预览版.这样才干持续改进.Growth是一个关于怎样成为优秀的Web Developer的APP--结合技能树.成长路线图.进阶书单.Web七日谈以及一些小測验. 它是我对 ...

  7. KLT 光流

    一 光流 光流的概念是Gibson在1950年首先提出来的.它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在 ...

  8. 【code】flex_进度条样式

    近期打算吧硬盘中的资料记录在博客中,实用的就当是个分享,无用的就当是个备份,还望大家不要见怪. 一共4个文件: JinDuTiaoItem.mxml: <?xml version="1 ...

  9. 李洪强漫谈iOS开发[C语言-044]-翻译数字

     李洪强漫谈iOS开发[C语言-044]-翻译数字

  10. Apple Swift学习教程

    翻译自苹果的官方文档:The Swift Programming Language. 简单介绍 今天凌晨Apple刚刚公布了Swift编程语言,本文从其公布的书籍<The Swift Progr ...