一、Buffer是啥?
- 一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类
- Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
二、 Buffer的子类
千言万语胜不过一图
从图知除boolean 外,基本数据类型都有缓冲区
三、缓冲区的基本属性
Buffer 中的重要概念:
- 容量 (capacity) :表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创建后不能更改。
- 限制 (limit):第一个不应该读取或写入的数据的索引,即位于 limit 后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。
- 位置 (position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制
- 标记 (mark)与重置 (reset):标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这个 position.
- 标记、位置、限制、容量遵守以下不变式: 0 <= mark <= position <= limit <= capacity
是不是有点懵逼咧,没事,看图就知道了
常用方法:
- capacity()----返回的是缓冲区容量的大小。
- position()----返回position的值大小。
- limit()----返回此缓冲区的限制值limit.
- mark()----标记缓冲区中位置。
- reset()----将缓冲区位置position设置为标记的位置。
- clear()----清空缓冲区,调用此方法将position设置为0,limit的值设置为缓冲区容量值,标记将被丢弃。
- flip()----调用此方法将limit设置当前position的值,position设置为0,有标记情况下,标记将被丢弃。
- rewind()----调用此方法将position值设置为0,标记将被丢弃.,limit值不会变化。
- remaining()----返回当前位置与限制limit之间的值,即limit-position的值。
- hasRemaing()—布尔类型,当前位置与限制值之间是否有元素。
- isDirect()----判断此缓冲区是否为直接缓冲区(调用allocateDirect()分配的是直接缓冲区,即不受JVM管控的堆外内存)。
其它一些方法请自行查看API
四、ByteBuffer缓冲区(以它为例子)
- 创建缓冲区
直接看代码
ByteBuffer buf1 = ByteBuffer.allocate(1024); //调用allocate()方法,创建直接缓冲区
ByteBuffer buf2 = ByteBuffer.allocateDirect(1024); //调用allocateDirect()方法,创建非直接缓冲区
- 如何读取缓冲区的数据
直接看图
五、直接缓冲区和非直接缓冲区???
说了老半天,它们两者如何理解呀呀呀!!!,别急,往下看!!!
理解干货来了:
- 直接缓冲区的创建是由操作系统方面的代码分配的,是JVM堆外的内存,直接分别缓冲区脱离了JVM的束缚,是IO操作方面的最佳选择,但创建和销毁直接缓冲区花销更大
- 如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
- 非直接缓冲区分配的是JVM堆内存。
- 创建非直接缓冲区向通道传输数据底层可能会先创建一个临时直接缓冲区,然后将非直接缓冲区数据放到临时直接缓冲区,使用临时缓冲区与IO进行交互,这会创建大量对象,但垃圾对象能得到及时回收
又瞎逼逼说了那么多,还是不懂咋搞,没事,看图哈
六、奉上代码(光说不练,歪把子)
package nio_study;
import java.nio.ByteBuffer;
/*
* 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
*
* 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
* ByteBuffer
* CharBuffer
* ShortBuffer
* IntBuffer
* LongBuffer
* FloatBuffer
* DoubleBuffer
*
* 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区
*
* 二、缓冲区存取数据的两个核心方法:
* put() : 存入数据到缓冲区中
* get() : 获取缓冲区中的数据
*
* 三、缓冲区中的四个核心属性:
* capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
* limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
* position : 位置,表示缓冲区中正在操作数据的位置。
*
* mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
*
* 0 <= mark <= position <= limit <= capacity
*
* 四、直接缓冲区与非直接缓冲区:
* 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
* 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
*/
public class TestBuffer {
public void test1(){
String str = "abcde";
//1. 分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("-----------------allocate()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//2. 利用 put() 存入数据到缓冲区中
buf.put(str.getBytes());
System.out.println("-----------------put()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//3. 切换读取数据模式
buf.flip();
System.out.println("-----------------flip()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//4. 利用 get() 读取缓冲区中的数据
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst, 0, dst.length));
System.out.println("-----------------get()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//5. rewind() : 可重复读
buf.rewind();
System.out.println("-----------------rewind()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
buf.clear();
System.out.println("-----------------clear()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
System.out.println((char)buf.get());
}
public void test2(){
String str = "abcde";
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put(str.getBytes());
buf.flip();
byte[] dst = new byte[buf.limit()];
buf.get(dst, 0, 2);
System.out.println(new String(dst, 0, 2));
System.out.println(buf.position());
//mark() : 标记
buf.mark();
buf.get(dst, 2, 2);
System.out.println(new String(dst, 2, 2));
System.out.println(buf.position());
//reset() : 恢复到 mark 的位置
buf.reset();
System.out.println(buf.position());
//判断缓冲区中是否还有剩余数据
if(buf.hasRemaining()){
//获取缓冲区中可以操作的数量
System.out.println(buf.remaining());
}
}
public void test3(){
//分配直接缓冲区
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
System.out.println(buf.isDirect());
}
}
七、分享结束
最后有兴趣一起交流的,可以关注我的公众号:这里你能够学到很实用的技巧,不是常用的我不说,公众号回复提取码即可获取以下学习资料啦啦啦啦,喜欢就拿去吧!!
-
Java web从入门到精通电子书
-
Python机器学习电子书
-
Python400集(北京尚学堂)
-
JavaScript项目案例、经典面试题
-
Java300集(入门、精通)
-
Java后端培训机构录集(同事培训内部提供)
参考资料:
图片来自:b站尚硅谷Java视频_NIO 视频教程
代码来自:b站尚硅谷Java视频_NIO 视频教程
侵立删