Buffer
Buffer
我们用原有 IO 读写文件应该不会陌生了,顺带回顾一下,大致两种:
1. 在 InputStream 或 OutputStream 上读写字节或字节数组,读 InputStream 时用是否返回 -1 来判断是否到达末尾。
2. 包装成 Reader/Writer 可以直接读写字符串,进一步包装到 BufferedReader/BufferedWriter 就可以按行读写了。readLine() 时看是否返回 null 断定是否读完了最后一行。
现在我们要用 NIO 来读写文件,肯定是要用到 Channel 和 Buffer 了。一句话描述过程就是从 FileInputStream 得到的 FileChannel 中读取数据到 Buffer 中,再处理 Buffer 中的数据。看代码:
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("C:/Java/jdk1.6.0_18/LICENSE");
// 得到文件通道
FileChannel fc = fis.getChannel();
// 分配与文件尺寸等大的缓冲区
ByteBuffer bf = ByteBuffer.allocate((int) fc.size());
// 整个文件内容全读入缓冲区,即是内存映射文件
fc.read(bf);
// 把缓冲中当前位置回复为零
bf.rewind();
// 输出缓冲区中的内容
while (bf.hasRemaining()) {
System.out.print((char) bf.get());
}
上面程序使用了一个与文件尺寸等大的缓冲区,正好能一次性把文件内容全部读入内存,如果文件过多将是十分耗费的内存的,所以我们可能须手工指定某个大小(如 1024,2048) 的缓冲区,然后分多次读入文件内容到缓冲区中。这时候程序就是下面那样子了:
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("C:/Java/jdk1.6.0_18/LICENSE");
// 得到文件通道
FileChannel fc = fis.getChannel();
// 指定大小为 1024 的缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
// 读取通道中的下一块数据到缓冲区中
// 缓冲区的 position 即为当前缓冲区中最后有效位置
while (fc.read(bf) != -1) {
// 把缓冲中当前位置回复为零,前把缓冲区的 limit 设置为之前 position 值
bf.flip();
// 输出缓冲区中的内容
while (bf.hasRemaining()) {
System.out.print((char) bf.get());
}
// 清理缓冲区,准备再次读取数据
bf.clear();
}
}
留意对缓冲区的 rewind()/flip()/clear() 操作所产生的影响,即对 position/limit/mark 等标志的影响。
最后提醒操作完之后,要关闭通道和输入流。
再来看看 NIO 怎么写文件
private static final int SIZE = 1024;
public static void main(String[] args) throws FileNotFoundException,
IOException {
FileOutputStream fos = new FileOutputStream("c:/nio.txt");
// 得到文件通道
FileChannel fc = fos.getChannel();
// 指定大小为 1024 的缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
// 要写入文件的字符串
String greeting = "Hello, Java NIO";
// 把以上字符串逐字放入缓冲区
for (int i = 0; i < greeting.length(); i++) {
bf.putChar(greeting.charAt(i));
}
// 记得执行这个方法,使得 position=0, limit=30, 才能写入正确的数据
// 否则 position 为 30, limit 为 1024,将会把 30 之后的全部空数据(0) 填到文件中
bf.flip();
// 缓冲区数据写入到文件中,会把缓冲区中从 position 到 limit 之间的数据写入文件
fc.write(bf);
fc.close(); // 关闭文件通道
fos.close(); // 关闭文件输出流
}
同样的,如果是写入中文内容,也需要进行字符集的相关处理。执行后在 C 盘根目录下产生 nio.tst 文件,内容就是 Hello, Java NIO。此代码的关键之处就是对缓冲的 flip() 调用,你可以在调试模式下观察到 flip() 方法调用前后,缓冲区 bf 的 position/limit 属性的变化。试着注释掉 flip() 代码,看看两次生成的 nio.tst 文件内容是不是大相径庭。
所以,要用好 NIO,缓冲区的 mark/position/limit/capacity 属性应理解,以及 clear()/flip()/rewind() 分别会怎么影响到以上属性。
还有,虽然说通道是双向的,字面上不像流那样区分输入通道或是输出通道,但实际通道也存在只读或只写的特性,例如由 FileInputStream.getChannel() 获得的通道是无法写入内容的,由 FileOutputStream.getChannel() 获得的通道是不能读的,否则会抛出相应的异常 NonWritableChannelException 和 NonReadableChannelException。而且 Buffer 也存在着是否只读的属性。
前面的代码在这里只是说明用 NIO 读写文件应如何处理,并不是说比起用旧 IO 流式的写法效率就更高了。NIO 的高效率只会体现在有些时候,并非任何时候都优于旧 IO,那是块操作和字节操作的区别,用 NIO 时要小心内存。伸手就能摘到的梨用不着搬个凳子,何况旧 IO 实现起来还更简洁些呢! (文/隔叶黄莺)
转自: http://www.360doc.com/content/12/0515/11/1542811_211144310.shtml
Buffer的更多相关文章
- Node.js:Buffer浅谈
Javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意.Node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该 ...
- java.IO输入输出流:过滤流:buffer流和data流
java.io使用了适配器模式装饰模式等设计模式来解决字符流的套接和输入输出问题. 字节流只能一次处理一个字节,为了更方便的操作数据,便加入了套接流. 问题引入:缓冲流为什么比普通的文件字节流效率高? ...
- 一点公益商城开发系统模式Ring Buffer+
一个队列如果只生产不消费肯定不行的,那么如何及时消费Ring Buffer的数据呢?简单的方案就是当Ring Buffer"写满"的时候一次性将数据"消费"掉. ...
- CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总
CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总 开始 总的来说,OpenGL应用开发者会遇到为如下三种数据创建Vertex Buffer Object的情形: ...
- golang bytes.Buffer Reset
func t() { a := []'} buf := new(bytes.Buffer) buf.Write(a) b := buf.Bytes() fmt.Println(b) buf.Reset ...
- 使用Ring Buffer构建高性能的文件写入程序
最近常收到SOD框架的朋友报告的SOD的SQL日志功能报错:文件句柄丢失.经过分析得知,这些朋友使用SOD框架开发了访问量比较大的系统,由于忘记关闭SQL日志功能所以出现了很高频率的日志写入操作,从而 ...
- directx12中vetex buffer、index buffer和constant buffer绑定piple line的时机
类别 时机 函数 建Heap vetex buffer 在Draw函数中 ID3D12GraphicsCommandList::IASetVertexBuffer 否 index buffer 在Dr ...
- JAVA NIO Buffer
所谓的输入,输出,就是把数据移除或移入缓冲区. 硬件不能直接访问用户控件(JVM). 基于存储的硬件设备操控的是固定大小的数据块儿,用户请求的是任意大小的或非对齐的数据块儿. 虚拟内存:使用虚 ...
- Circular Buffer
From:http://bradforj287.blogspot.com/2010/11/efficient-circular-buffer-in-java.html import java.util ...
- Buffer类
输入流中可以通过缓冲区来加大读取的效率,sun公司感觉可以加快执行效率,他就为我们提供了一个类来操作缓存区. Buffer来头的类:所有缓冲流都是以Buffer开头的: 学习缓冲流的作用: Buffe ...
随机推荐
- AutoCAD 2014 win 32bit破解版
AutoCAD 2014 win 32bit破解版 百度云盘:http://pan.baidu.com/s/1nu2u6Hr
- Ehcache(2.9.x) - API Developer Guide, Key Classes and Methods
About the Key Classes Ehcache consists of a CacheManager, which manages logical data sets represente ...
- UITableView详解(转)
首先.对UITableView进行讲解,下面有对它进行实际的应用 UITableView 显示大型内容的列表 单行,多列 垂直滚动,没有水平滚动 大量的数据集 性能强大,而且普遍存在于iPhone的应 ...
- BUG修改纪录
刚进入现在的公司,接手了一个遗留的项目,BUG频出,最近一个星期都在加班改BUG,身心疲惫,为了 现在,将来不会再被相同BUG困扰,特来纪录一下. 1.数据库设计字段时,对于int等类型最好设置默认值 ...
- Fragment里面的ViewPager嵌套subFragment,主Fragment切换的时候subFragment出现空白Fragment的Bug
Fragment第二次进入不显示,主要是第二次加载的时候重复调用了onCreateView()这个方法,重新new了一个pageadapter导致子fragment不显示,问题的解决方法就是在onCr ...
- 第四篇、CocoaPods 镜像的更新 原来的淘宝镜像已经不再更新
在开发应用,我们常常使用cocoaPods来管理第三方框架,但是原来的淘宝的镜像不更新了 新的镜像地址:https://gems.ruby-china.org/
- 第三十篇、iOS开发中常用的宏
//字符串是否为空 #define kStringIsEmpty(str) ([str isKindOfClass:[NSNull class]] || str == nil || [str leng ...
- MD5和Base64介绍与应用
MD5:概念:MD5是一种不可逆的消息摘要算法.为计算机安全领域广泛使⽤的一种散列函数, 用以提供消息的完整性保护.效果:把一个任意长度的字节串变换成⼀定⻓度的⼗六进制数字串. 目的是让⼤容量信息在⽤ ...
- Swift类型检查与转换
继承会发生在子类和父类中,如图所示,是一系列类的继承关系类图,Person是类层次结构中的根类,Student是Person的直接子类,Worker是Person的直接子类.这个继承关系类图的具体实现 ...
- Cocoa框架中的内存分区
NSObject对象类实例为我们提供了两种分配内存的方式:alloc和allocWithZone:.alloc的实现也是为了调用allocWitZone:,它传入了一个默认的zone.所以明确的说,N ...