Java NIO之Buffer的使用
目录
Buffer简介Buffer的核心属性Buffer的创建与使用(ByteBuffer为例)- 总结
- 参考资料
Buffer简介
缓冲区(Buffer):本质上是一个数组,用于临时保存、写入以及读取数据。在Java NIO中,
该内存块包含在NIO Buffer对象当中,NIO Buffer对象还提供了一组接口来访问该内存块。
根据数据类型的不同,Java为除了boolean类型之外的其余7种基本类型提供了相应类型的缓冲区,
分别是ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、
FloatBuffer、DoubleBuffer。他们都继承自抽象类Buffer类,他们的管理方式也都几乎一样。
UML类图如下:

Buffer的核心属性
BUffer类的部分实现如下:
public abstract class Buffer {
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
//构造方法
Buffer(int mark, int pos, int lim, int cap) { // package-private
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
this.mark = mark;
}
}
/**
* Returns this buffer's capacity.
*
* @return The capacity of this buffer
*/
//返回这个Buffer的容量
public final int capacity() {
return capacity;
}
/**
* Returns this buffer's position.
*
* @return The position of this buffer
*/
//返回这个Buffer中当前的位置(当前操作数)
public final int position() {
return position;
}
/**
* Returns this buffer's limit.
*
* @return The limit of this buffer
*/
//返回当前Buffer中可以被操作的元素的个数
public final int limit() {
return limit;
}
/**
* Sets this buffer's mark at its position.
*
* @return This buffer
*/
//记录当前position的位置
public final Buffer mark() {
mark = position;
return this;
}
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
}
其中定义了四个Buffer属性,对应的描述如下
| 属性 | 描述 |
|---|---|
| capacity | 容量;用于描述这个Buffer大小,即创建的数组的长度,一旦声明不可以被改变 |
| position | 位置,表示当前缓冲区中正在操作的数据的位置,在切换读取时会将其置0 |
| limit | 界限、限制;表示当前缓冲区中可以操作的数据的大小,默认情况下为Buffer的大小,切换为读取模式后为数组中元素的个数(准确的说时切换之前position的值) |
| mark | 标记;用于记录当前position的位置,后续操作过程中可以使用reset()方法将position还原至最后一次mark的位置 |
Buffer的创建与使用(ByteBuffer为例)
Buffer的创建
在Java NIO中可以使用对应Buffer类的allocate()或者allocateDirect()静态方法创建。
//使用allocate()创建
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//使用allocateDirect()创建
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
而Buffer的本质是一个数组,创建时需要指定数组的大小
Buffer的使用
Buffer的使用一般分为四个步骤
- 向
Buffer中写入数据 - 将
Buffer切换为读取模式 - 读取
Buffer - 将
Buffer清空,供后续写入使用
1. 写如数据
//使用put()方法向Buffer中写入数据
byteBuffer.put("bmilk".getBytes());
//使用Channel#read()向Buffer中写入数据
channel.read(byteBuffer);
2. 将Buffer切换为读取模式
可以通过调用flip()方法将Buffer从写模式切换到读模式。
byteBuffer.flip()
调用flip()方法会将position设回0,并将limit设置成之前position的值。
即,现在使用position标记读的位置,limit表示之前写进了多少个byte,也就是现在
能读取多少个byte等。
3. 读取Buffer
读取Buffer有两种方式:
- 从
Buffer种读取数据到Channel - 使用
get()方法从Buffer种读取数据
//从Buffe中将数据写入通道
inChannel.write(byteBuffer)
//使用get()方法从BUffer中读取数据
byte[] bytes=new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
4. 将Buffer清空,供后续写入使用
使用clear()清空缓冲区,清空缓冲区只是使各个指针恢复初始位置,
更具体的说是position设置为0,limit设置为容量的初始大小。
并不会真实清空其中数据,但是可以通过后续的写覆盖之前的数据
byteBuffer.clear()
其他的一些方法
- 使用
rewind()从Buffer重复读取数据
//使用`rewind()`从`Buffer`重复读取数据
//Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。
//limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。
Buffer rewind = byteBuffer.rewind();
compact()方法
clear()会使使各个指针恢复初始位置,但是实际中可能存在部分数据还没有被使用,而后续需要使用。
又必须清理一部分Buffer的空间,compact()方法会将所有未读数据拷贝到Buffer的起始处,
然后将position指针设置到最后一个未读元素的后面,现在Buffer可以进行写数据,
但是不会覆盖前面的未读的数据。
mark()方法与reset()方法
通过调用Buffer.mark()方法,可以标记Buffer中的当前的position。之后可以通过调用Buffer.reset()方法恢复到这个position。
//使用mark标记当前的position位置
byteBUffer.mark()
//使用reset方法使position指针返回这个位置
byteBuffer.reset()
4.equals()方法与compareTo()方法
当需要比较两个Buffer时可以使用equals()方法与compareTo()方法。
equals()方法判断两个方式是否相等,当满足下列条件时,表示两个Buffer相等
- 有相同的类型(
byte、char、int等)Buffer中剩余的byte、char等的个数相等。- \(\color{#FF3030}{`Buffer`中所有剩余的`byte`、`char`等都相同}\)
compareTo()方法比较两个两个Buffer的大小,仅比较剩余元素(byte、char等)
如果满足下列条件,则认为一个Buffer“小于”另一个Buffer:
- 第一个不相等的元素小于另一个Buffer中对应的元素
- 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。
直接缓冲区与非直接缓冲区
- 非直接缓冲区:通过
allocate()方法分配缓冲区,将缓冲区建立在JVM内存中 - 直接俄缓冲区:通过
allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以在某些情况下提高效率
非直接缓冲区
- 非直接缓冲区数据流向图

直接缓冲区
- 直接缓冲区数据流向图

直接缓冲区(物理内存映射文件):相比非直接缓冲区省略了copy的过程,所以说直接缓区可以一定程度上提高效率
弊端:
- 开辟空间时资源消耗大
- 不安全,
java程序将数据写入物理内存映射文件中,之后数据将不受Java程序控制,
什么时候写入硬盘无法控制(由操作系统控制),当垃圾回收机制释放引用后才能断开与之的连接
小结
- 缓冲区要么是直接的,要么是非直接的如果为直接字节缓冲区,则
java虚拟机会见最大努力直接在此缓冲区上执行本机I/O。
也就是说,每次调用基础操作系统的I/O之前或之后,虚拟机都回尽量避免将缓冲区的内容复制到中间缓冲区或者从中间缓冲区中复制内容。 - 直接字节缓冲区可以通过调用此类的
allocateDirect()工厂方法来创建,
此方法返回的缓冲区进行分配和取消分配所需的程本通常高于非直接缓冲区,
直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此他们对应用程序内存需求造成的影响可能并不明显,
所以建议直接缓冲区主要分配给易受基础系统的本机I/O操作影响的大型、持久得缓冲区。
一般情况下,最好尽在直接缓冲区能在程序性能方面带来明显好处时分配他们。 - 直接字节缓冲区还可以通过
FileChannel的map()方法,将文件区域直接映射到内存中来创建,
该方法返回MappedByteBuffer。Java的实现有助于JNI从本地及代码创建直接字节缓冲区,
如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域。
则试图访问该区域不会更改缓冲区的内容,并且将会在访问期间或稍后的时间导致抛出不确定的异常 - 字节缓冲区是直接缓冲区还是非直接缓冲区可以通过调用其
isDirect()方法来确定,提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
总结
本文简单介绍了Buffer的种类,并对常用方法进行乐简单的介绍
参考资料
Java NIO之Buffer的使用的更多相关文章
- Java NIO 之 Buffer
Java NIO 之 Buffer Java NIO (Non Blocking IO 或者 New IO)是一种非阻塞IO的实现.NIO通过Channel.Buffer.Selector几个组件的协 ...
- Java NIO之Buffer(缓冲区)
Java NIO中的缓存区(Buffer)用于和通道(Channel)进行交互.数据是从通道读入缓冲区,从缓冲区写入到通道中的. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这 ...
- JAVA NIO简介-- Buffer、Channel、Charset 、直接缓冲区、分散和聚集、文件锁
IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. Java标准io回顾 在Java1.4之前的I/O系统中,提供 ...
- java nio之Buffer(一)
Buffer是一个包装了基本数据元素数组的对象,它以及它的子类定义了一系列API用于处理数据缓存. 一.属性 Buffer有四个基本属性: 1.capacity 容量,buffer能够容纳的最大元素 ...
- Java NIO -- 缓冲区(Buffer)的数据存取
缓冲区(Buffer): 一个用于特定基本数据类型的容器.由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类.Java NIO 中的 Buffer 主要用于与 NIO 通道进行 ...
- Java NIO 之 Buffer(缓冲区)
一 Buffer(缓冲区)介绍 Java NIO Buffers用于和NIO Channel交互. 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels. Bu ...
- 【Java nio】buffer
package com.slp.nio; import org.junit.Test; import java.nio.ByteBuffer; /** * Created by sanglp on 2 ...
- java nio之Buffer
一.JAVA NIO 是在和channel交互的时候使用的.Channel将数据读入缓冲区,然后我们又从缓冲区访问数据.写数据时,首先将要发送的数据按顺序填入缓冲区.基本上,缓冲区只是一个列表,它的所 ...
- Java NIO:Buffer、Channel 和 Selector
Buffer 一个 Buffer 本质上是内存中的一块,我们可以将数据写入这块内存,之后从这块内存获取数据. java.nio 定义了以下几个 Buffer 的实现,这个图读者应该也在不少地方见过了吧 ...
- JAVA NIO缓冲区(Buffer)------ByteBuffer常用方法
参考:https://blog.csdn.net/xialong_927/article/details/81044759 缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I ...
随机推荐
- Java实现选号码
选号码 Description CF打算换个手机号码,但是他去营业厅选号码的时候却把移动的客服小姐烦得不行,因为他太挑三捡四啦. 对于一个手机号的后六位数字(前面五位他就无所谓了)CF有很严格的要求, ...
- java实现第六届蓝桥杯居民集会
居民集会 蓝桥村的居民都生活在一条公路的边上,公路的长度为L,每户家庭的位置都用这户家庭到公路的起点的距离来计算,第i户家庭距起点的距离为di. 每年,蓝桥村都要举行一次集会.今年,由于村里的人口太多 ...
- java实现第七届蓝桥杯阶乘位数
阶乘位数 阶乘位数 9的阶乘等于:362880 它的二进制表示为:1011000100110000000 这个数字共有19位. 请你计算,9999 的阶乘的二进制表示一共有多少位? 注意:需要提交的是 ...
- 「从零单排canal 01」 canal 10分钟入门(基于1.1.4版本)
1.简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据 订阅 和 消费.应该是阿里云DTS(Data Transfer Servi ...
- 阿里云高级技术专家空见: CDN的数据化之路
想要实现优质高速的互联网视频服务,一定离不开高质量的内容分发网络服务,就是我们常说的CDN,在10月13日云栖大会视频多媒体分论坛上,阿里云高级技术专家空见为大家讲解了CDN服务过程中,数据处理.安全 ...
- K8S-PV和PVC的实践
一.什么是PV和PVC? PV的全称是Persistent Volume,翻译过来为持久化存储卷,是对底层的共享存储的一种抽象,PV由管理员进行创建和配置,主要含存储能力.访问模式.存储类型.回收策略 ...
- tensorflow2.0学习笔记第一章第五节
1.5简单神经网络实现过程全览
- Python 抓取网页tag操作
1. 获取操作tag 获取操作tag的接种方式: soup.find_all(name=None, attrs={}, recursive=True, text=None, limit=None, * ...
- netty实现消息中心(一)思路整理
一.需求 需要实现直播间的以下功能: 群发消息(文本.图片.推荐商品) 点对点私发消息(文本.图片.推荐商品) 单个用户禁言 全体用户禁言 撤回消息 聊天记录持久化 二.技术实现 服务端消息中心采用n ...
- 给博客使用Butterfly主题并部署到GitHub服务器
目录 前言 一.安装Butterfly主题 二.将本地博客部署到GitHub服务器 三.将个人域名与GitHub绑定 前言 安装完Hexo框架后,自带的主题在thems文件夹下可以查看,应用后界面: ...