缓冲区即可以用来输入也可以用来输出,这一点和流不同,流只能向一个方向传递数据。
ByteBuffer是Java的NIO中普遍使用的用于接收和发送消息的缓冲区。在读写数据时,它具有内部状态来跟踪缓冲区的当前位置。
1.缓冲区的内部状态:
capacity: 缓冲区的元素总数(不可修改)。
position: 下一个要读写的元素位置(从0开始)。
limit: 第一个不可读写的位置。
mark: 用户选定的position的前一个位置或0。
注:position和limit之间的距离指示了可读/存的字节数。
boolean hasRemaining():当缓冲区至少还有一个元素时,返回true。
int remaining():position和limit之间字节个数。
所以,他们满足的关系是这样的:0<=mark<=position<=limit<=capacity
reset():将position的值还原成上传调用mark()方法后的position的值。
2.接下来说说创建buffer的两种模式的区别:
创建ByteBuffer可以使用allocate或者wrap,就像下面这样:
ByteBuffer allocate(int capacity)
ByteBuffer allocateDirect(int capacity)
ByteBuffer wrap(int capacity)
ByteBuffer wrap(byte[] array,int offset,int length)
注:创建的缓冲区都是定长的,大小无法改变。若发现刚创建的缓冲区容量太小,只能重新创建一个合适的。特别注意一下 ByteBuffer wrap(byte[] array,int offset,int length),这样创建的ByteBuffer的capacity和array的大小是一样的,position是offset,limit是offset+length,position之前和limit之后的数据依然可以访问到。
像这样:
public class Singleton { public static void main(String args[]){ byte arr[]=new byte[100]; ByteBuffer buffer=ByteBuffer.wrap(arr,3,25); System.out.println("Capacity is: "+buffer.capacity()); System.out.println("Position is: "+buffer.position()); System.out.println("limit is: "+buffer.limit()); } } //结果: Capacity is: 100 Position is: 3 limit is: 28
接下来说一下allocate和wrap的区别:
wrap只是简单地创建一个具有指向被包装数组的引用的缓冲区,该数组成为后援数组。对后援数组中的数据做的任何修改都将改变缓冲区中的数据,反之亦然。
public static void main(String args[]){ byte arr[]=new byte[100]; //将arr数组全部置为1 Arrays.fill(arr, (byte)1); ByteBuffer buffer=ByteBuffer.wrap(arr,3,25); //对后援数组中的数据做的任何修改都将改变缓冲区中的数据 arr[0]=(byte)2; buffer.position(0); System.out.println(buffer.get()); //在缓冲区上调用array()方法即可获得后援数组的引用。 System.out.println(Arrays.toString(buffer.array())); } //运行结果: 2 [2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ....]//总共100个元素
allocate则是创建了自己的后援数组,在缓冲区上调用array()方法也可获得后援数组的引用。通过调用arrayOffset()方法,甚至可以获取缓冲区中第一个元素在后援数组的偏移量。但是使用wrap创建的ByteBuffer,调用arrayOffset永远是0。
public static void main(String args[]){ ByteBuffer buffer=ByteBuffer.allocate(100); //对后援素组的修改也可以反映到buffer上 byte arr[]=buffer.array(); arr[1]=(byte)'a'; buffer.getInt(); //不会影响arrayoffset,why? System.out.println(Arrays.toString(buffer.array())); System.out.println(buffer.arrayOffset()); } //运行结果: [0, 97, 0, 0, 0, 0, 0, 0,...]//总共100个元素 0
ByteBuffer allocateDirect(int capacity)创建的叫直接缓冲区,I/O速度更快。可以通过isDirect()方法查看一个缓冲区是否是直接缓冲区。由于直接缓冲区是没有后援数组的,所以在其上面调用array()或arrayOffset()都会抛出UnsupportedOperationException异常。注意有些平台或JVM可能不支持这个创建直接缓冲区。
3.下来我们看看ByteBuffer的get和put方法。
put和get方法有很多重载模式,具体可以参考API。但是切记无论是put还是get,position的值都会递增。看一下如下代码就一目了然了:
public class Singleton { public static void main(String args[]){ ByteBuffer buffer=ByteBuffer.allocate(10); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); buffer.put("a".getBytes()); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); buffer.putInt(2); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); System.out.println(“it is: ”+buffer.getChar()); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); } } //结果: position is 0, capacity id 10, limit is 10 position is 1, capacity id 10, limit is 10 position is 5, capacity id 10, limit is 10 it is: position is 7, capacity id 10, limit is 10
可见不是我们所想的那样,一getChar就可以得到char。ByteBuffer很灵活,需要通过设置position来读取。无论是get还是set,position都会增加。
当然了以byte为单位,int(4字节),char(2字节)。
要想获得我们具体的值可以这样:
public class Singleton { public static void main(String args[]){ ByteBuffer buffer=ByteBuffer.allocate(10); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); buffer.put("a".getBytes()); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); buffer.putInt(2); System.out.printf("position is %d, capacity id %d, limit is %d\n",buffer.position(),buffer.capacity(),buffer.limit()); buffer.position(0); System.out.println("byte is: "+(char)buffer.get()); System.out.println("int is: "+buffer.getInt()); } } //结果: position is 0, capacity id 10, limit is 10 position is 1, capacity id 10, limit is 10 position is 5, capacity id 10, limit is 10 byte is: a int is: 2
以上说的是基于相对位置的get和put,接下来我们看一下基于绝对位置的get和put:
byte get(int index)
ByteBuffer put(int index,byte b)
绝对位置形式的get和put不会改变position的值。基于绝对位置的get()和put()以指定的索引位置为参数,从该位置读取或者向该位置写入数据,不会改变position的值。
除此之外,还可以指定大端存储还是小端存储。
ByteOrder order()
ByteBuffer order(ByteOrder order)
4.ByteBuffer准备了几个方法利于我们设置position和limit。
clear() 将position设置为0,limit设置为capacity,mark未定义。
flip() 将posiion设置为0,limit设置为position,mark未定义。
rewind() 将position设置为0,limit没有改变,mark未定义。
5.看一下,ByteBuffer提供的一些函数:
public static void main(String args[]){ ByteBuffer buffer=ByteBuffer.allocate(100); //对后援素组的修改也可以反映到buffer上 byte arr[]=buffer.array(); Arrays.fill(arr, (byte)2); ByteBuffer newBuffer=buffer.duplicate(); arr[1]=3; byte arr1[]=new byte[100]; buffer.position(0);//若这没有标识position为0,则会抛出java.nio.BufferUnderflowException,是从position到limit复制的 buffer.get(arr1); arr1[0]=7;//不会影响到ByteBuffer的后援数组 System.out.println(Arrays.toString(newBuffer.array())); System.out.println(buffer.position()); System.out.println(newBuffer.position()); System.out.println(Arrays.toString(arr1)); } //运行结果: [2, 3, 2, 2, 2, 2, 2, 2....//100个 100 0 [7, 3, 2, 2, 2, 2, 2,.....//100个
public static void main(String args[]){ ByteBuffer buffer=ByteBuffer.allocate(10); byte arr[]=buffer.array(); for(int i=0;i<arr.length;i++){ arr[i]=(byte)i; } buffer.position(2); buffer.limit(6); arr[3]=(byte)66; //这个是为了证明:buffer和newBuffer共享后援数组 //slice返回原ByteBuffer的一个镜像,所有改变互相可见。position和limit独立 //返回的ByteBuffer的position永远为0,limit为原ByteBuffer的(limit-position) //返回的ByteBuffer的capacity为(limit-position) //但是调用ByteBuffer.array返回的是同一个完整的数组,就是原ByteBuffer的 //后援数组 ByteBuffer newBuffer=buffer.slice(); System.out.println(buffer.position()); System.out.println(buffer.limit()); System.out.println(buffer.capacity()); System.out.println(newBuffer.position()); System.out.println(newBuffer.limit()); System.out.println(newBuffer.capacity()); System.out.println(Arrays.toString(buffer.array())); System.out.println(Arrays.toString(newBuffer.array())); System.out.println(buffer.get()); System.out.println(newBuffer.get()); //System.out.println(newBuffer.capacity(8)); capacity是无法这样设置的。 }
相关推荐
使用nio byteBuffer 实现按行读取文件(大文件) 在window/linux/macOS上均测试通过 对于中文乱码也已处理成功 完整注释,可随需求更改 有问题请邮件:mly610865580@126.com
设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件结束 这样字节的判断,然后 返回行 如果 到达 fbb的结尾 还没有结束,就再通过nio读取一段字节,继续处理。 ...
Chronicle Bytes具有与Java NIO的ByteBuffer类似的用途,但具有一些扩展。 API支持。 64位大小 UTF-8和ISO-8859-1编码的字符串。 线程安全关闭堆内存操作。 通过引用计数确定性地释放资源。 压缩数据类型,...
实工作中常常遇到的 I/O 问题,并了解这些新特性如何对响应速率、可伸缩性和可靠性产生直接影 响。 NIO API 是对 1.3 版 I/O 特性的补充而非取代,因此,何时使用新的 API,何时老的 1.3 版 I/O API 更适合特定应用...
NULL 博文链接:https://chinaestone.iteye.com/blog/468138
NULL 博文链接:https://zheng12tian.iteye.com/blog/1094811
Java NIO(New IO)是 Java IO API 的替代方案(从 Java 1.4 开始), 它替代标准的 Java IO 和 Java 网络 API。 Java NIO 提供了与标准 IO 不同的 IO 工作方式。 英文原版由知名的Jenkov编写而成。原文链接:...
java nio 中文PDF,
使用 io和nio 各个方式进行文件拷贝;...使用nio+byteBuffer 实现按行读取文件(大文件),在window/linux/macOS上均测试通过,对于中文乱码也已处理成功 完整注释,可随需求更改 有问题请邮件:mly610865580@126.com
JavaNIO中文教程1.pdf
可以动态演示nio中buffer的相关操作,直观看到各种重要数据的变化,方便大家更好的理解nio的buffer模型
Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...
Java NIO,Ron Hitchens 著,中文版 裴小星 译,Pro Java 7 NIO.2,Anghel Leonard 著,pdf文字版带书签,无安全限制
讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用
赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
NIO入门.chm NIO入门.chm NIO入门.chm
Java NIO,Ron Hitchens 著,中文版 裴小星 译,pdf文字版带书签,无安全限制
java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...
新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就...
NIO需要了解的一些概念NIO需要了解的一些概念