java学习-NIO(二)Buffer
当我们需要与 NIO Channel 进行交互时, 我们就需要使用到 NIO Buffer, 即数据从 Buffer读取到 Channel 中, 并且从 Channel 中写入到 Buffer 中。缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
缓冲区基础
Buffer 类型有:
缓冲区是包在一个对象内的基础数据的数组,Buffer类相比一般简单数组而言其优点是将数据的内容和相关信息放在一个对象里面,这个对象提供了处理缓冲区数据的丰富的API。
所有缓冲区都有4个属性:capacity、limit、position、mark,并遵循:capacity>=limit>=position>=mark>=0,下面是对这4个属性的解释:
- Capacity: 容量,即可以容纳的最大数据量;在缓冲区创建时被设定并且不能改变
- Limit: 上界,缓冲区中当前数据量
- Position: 位置,下一个要被读或写的元素的索引
- Mark: 标记,调用mark()来设置mark=position,再调用reset()可以让position恢复到标记的位置即position=mark
我们通过一个简单的操作流程来说明buffer的使用,下图是新创建的容量为10的缓冲区逻辑视图:
然后进行5次调用put:
buffer.put((byte)’A’).put((byte)’B’).put((byte)’C’).put((byte)’D’).put((byte)’E’)
5次调用put之后的缓冲区为:
现在缓冲区满了,我们必须将其清空。我们想把这个缓冲区传递给一个通道,以使内容能被全部写出,但现在执行get()无疑会取出未定义的数据。我们必须将 posistion设为0,然后通道就会从正确的位置开始读了,但读到哪算读完了呢?这正是limit引入的原因,它指明缓冲区有效内容的未端。这个操作 在缓冲区中叫做翻转:buffer.flip()。
Buffer的基本用法
使用Buffer读写数据一般遵循以下四个步骤:
- 写入数据到Buffer
- 调用flip()方法
- 从Buffer中读取数据
- 调用clear()方法或者compact()方法
当向buffer写入数据时,buffer会记录下写了多少数据。
一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。
一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
下面我们看一段程序来看一下Buffer的基本用法:
public static void readFile(String fileName) {
RandomAccessFile aFile = null;
try {
//文件流
aFile = new RandomAccessFile(fileName, "rw");
//将文件输入到管道
FileChannel inChannel = aFile.getChannel();
//为buffer分配1024个字节大小的空间
ByteBuffer buf = ByteBuffer.allocate(1024);
//将buffer中的内容读取到管道中
int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {
//反转buffer,将写模式改为读模式
buf.flip();
while (buf.hasRemaining()) {
//获取buffer中的数据
System.out.print((char) buf.get());
}
//将上次分配的1024字节的内容清空,为下次接收做准备
buf.clear();
//管道重新读取buffer中的内容
bytesRead = inChannel.read(buf);
}
aFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
字节缓冲区
我们将进一步观察字节缓冲区。所有的基本数据类型都有相应的缓冲区类(布尔型除外),但字节缓冲区有自己的独特之处。字节是操作系统及其I/O设备使用的基本数据类型。当在JVM和操作系统间传递数据时,将其他的数据类型拆分成构成它们的字节是十分必要的。如我们在后面的章节中将要看到的那样,系统层次的I/O面向字节的性质可以在整个缓冲区的设计以及它们互相配合的服务中感受到。
直接缓冲区
我们知道操作系统是在内存中进行I/O操作,这些内存区域,就操作系统方面而言,是相连的字节序列。于是,毫无疑问,只有字节缓冲区有资格参与I/O操作。即操作系统会直接存取进程,那么我们现在在JVM中进行操作,java中的内存空间是由JVM直接进行管理,但是在JVM中,字节数组可能不会在内存中连续存储,或者无用存储单元收集可能随时对其进行移动,这就不能保证I/O操作的目标是连续的。
出于这一原因,引入了直接缓冲区的概念。直接缓冲区被用于与通道和固有I/O例程交互。它们通过使用固有代码来告知操作系统直接释放或填充内存区域,对用于通道直接或原始存取的内存区域中的字节元素的存储尽了最大的努力。
直接字节缓冲区通常是I/O操作最好的选择。在设计方面,它们支持JVM可用的最高效I/O机制。非直接字节缓冲区可以被传递给通道,但是这样可能导致性能损耗。通常非直接缓冲不可能成为一个本地I/O操作的目标。如果您向一个通道中传递一个非直接ByteBuffer对象用于写入,通道可能会在每次调用中隐含地进行下面的操作:
- 创建一个临时的直接ByteBuffer对象。
- 将非直接缓冲区的内容复制到临时缓冲中。
- 使用临时缓冲区执行低层次I/O操作。
- 临时缓冲区对象离开作用域,并最终成为被回收的无用数据。
视图缓冲区
就像我们已经讨论的那样,I/O基本上可以归结成组字节数据的四处传递。在进行大数据量的I/O操作时,很又可能你会使用各种ByteBuffer类去读取文件内容,接收来自网络连接的数据,等等。一旦数据到达了你的ByteBuffer,您就需要查看它以决定怎么做或者在将它发送出去之前对它进行一些操作。ByteBuffer类提供了丰富的API来创建视图缓冲区。
视图缓冲区通过已存在的缓冲区对象实例的工厂方法来创建。这种视图对象维护它自己的属性,容量,位置,上界和标记,但是和原来的缓冲区共享数据元素。但是ByteBuffer类允许创建视图来将byte型缓冲区字节数据映射为其它的原始数据类型。例如,asLongBuffer()函数创建一个将八个字节型数据当成一个long型数据来存取的视图缓冲区。
但是使用视图缓冲区的话,一旦ByteBuffer对于视图的维护对象产生非常规行的使用,那么对于工厂方法创建的缓冲区而言,asLongBuffer()函数就不在使用这个视窗,那么这个8字节的数据当成一个long类型的数据类型来存取的数据视图。
java学习-NIO(二)Buffer的更多相关文章
- java学习-NIO(五)NIO学习总结以及NIO新特性介绍
我们知道是NIO是在2002年引入到J2SE 1.4里的,很多Java开发者比如我还是不知道怎么充分利用NIO,更少的人知道在Java SE 7里引入了更新的输入/输出 API(NIO.2).但是对于 ...
- java学习之二维数组
java当中的二维数组,存储一组比较特殊的对象.他存储一个数组,同时存储的数组当中又存储着元素. java二维数组的声明方式一: class Arr2Demo { public static void ...
- java学习(二)
学号 20189214 <Java程序设计>第二周学习总结 教材学习内容总结 java类 创建java对象需要类似的模板,即类(class) java对象也拥有属性和能够执行的动作. 属性 ...
- 类与对象 - Java学习(二)
弄清楚类与对象的本质与基本特征,是进一步学习面向对象编程语言的基本要求.面向对象程序设计与面向过程程序设计在思维上存在着很大差别,改变一种思维方式并不是一件容易的事情. 一.面向对象程序设计 程序由对 ...
- Java学习笔记二十九:一个Java面向对象的小练习
一个Java面向对象的小练习 一:项目需求与解决思路: 学习了这么长时间的面向对象,我们只是对面向对象有了一个简单的认识,我们现在来做一个小练习,这个例子可以使大家更好的掌握面向对象的特性: 1.人类 ...
- java学习-NIO(四)Selector
这一节我们将探索选择器(selectors).选择器提供选择执行已经就绪的任务的能力,这使得多元 I/O 成为可能.就像在第一章中描述的那样,就绪选择和多元执行使得单线程能够有效率地同时管理多个 I/ ...
- java学习-NIO(三)Channel
通道(Channel)是java.nio的第二个主要创新.它们既不是一个扩展也不是一项增强,而是全新.极好的Java I/O示例,提供与I/O服务的直接连接.Channel用于在字节缓冲区和位于通道另 ...
- java学习-NIO(一)简介
I/O简介 在 Java 编程中,直到最近一直使用 流 的方式完成 I/O.所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节.流 I/O 用于与外部世界接触. ...
- 数组排序、递归——(Java学习笔记二)
升序: 选择排序: 选定一个元素,一次和后面的元素相比较,如果选定的元素大雨后面的比较元素,就交换位置 先出现最小值,最后出现最大值. public stat ...
随机推荐
- Hive入门(四)查询优化
1 本地模式 0.7版本后Hive开始支持任务执行选择本地模式(local mode). 大多数的Hadoop job是需要hadoop提供的完整的可扩展性来处理大数据的.不过,有时hive的输入数据 ...
- Coderforces 633D:Fibonacci-ish(map+暴力枚举)
http://codeforces.com/problemset/problem/633/D D. Fibonacci-ish Yash has recently learnt about the ...
- Codeforces 757B:Bash's Big Day(分解因子+Hash)
http://codeforces.com/problemset/problem/757/B 题意:给出n个数,求一个最大的集合并且这个集合中的元素gcd的结果不等于1. 思路:一开始把素数表打出来, ...
- LightGBM,面试会问到的都在这了(附代码)!
1. LightGBM是什么东东 不久前微软DMTK(分布式机器学习工具包)团队在GitHub上开源了性能超越其他boosting工具的LightGBM,在三天之内GitHub上被star了1000次 ...
- ajax 前端发含有列表的数据
在前端页面也可以给后端发送一个包含列表的数据 html <body> <h3>index页面 </h3> <input type="text&quo ...
- Linux命令学习-mkdir命令
Linux中,mkdir命令的全称是make directory,即创建目录的意思. 假设当前处于wintest用户的主目录,路径为 /home/wintest ,存在文件夹testA,进入testA ...
- golang开发:类库篇(三)命令行工具cli的使用
为什么要使用命令行 觉得这个问题不应该列出来,又觉得如果初次进行WEB开发的话,可能会觉得所有的东西都可以使用API去做,会觉得命令行没有必要. 其实,一个生产的项目命令行是绕不过去的.比如运营需要导 ...
- 嵊州D2T1 “我只是来打个电话”
嵊州D2T1 “我只是来打个电话” 精神病院有一个这样的测试. 给出一个正整数集合,集合中的数各不相同,然后要求病人回答: 其中有多少个数,恰好等于集合中另外两个(不同的)数之和? 回答正确的人,即可 ...
- 【模拟】(正解树状数组)-校长的问题-C++-计蒜客
描述 学校中有 n 名学生,学号分别为 1 - n.再一次考试过后,学校按照学生的分数排了一个名次(分数一样,按照名字的字典序排序).你是一名老师,你明天要和校长汇报这次考试的考试情况,校长询问的方式 ...
- Keil debug command SAVE 命令保存文件的解析
简介 使用 Keil debug 很方便,把内存中的一段区域 dump 出来也很方便,例如使用命令 SAVE filepath startAddr, endAddr, typeCode .但是要查看 ...