ByteBuffer 字节缓冲区

| HeapByteBuffer | 在jvm堆上面的一个buffer,底层的本质是一个数组 | 由于内容维护在jvm里,所以把内容写进buffer里速度会快些;并且,可以更容易回收 |
| DirectByteBuffer | 底层的数据其实是维护在操作系统的内存中,而不是jvm里,DirectByteBuffer里维护了一个引用address指向了数据,从而操作数据 | 跟外设(IO设备)打交道时会快很多,因为外设读取jvm堆里的数据时,不是直接读取的,而是把jvm里的数据读到一个内存块里,再在这个块里读取的,如果使用DirectByteBuffer,则可以省去这一步,实现zero copy |
ByteBuffer的属性
byte[] buff //buff即内部用于缓存的数组。
position //当前读取的位置。
mark //为某一读过的位置做标记,便于某些时候回退到该位置。
capacity //初始化时候的容量。是它所包含的元素的数量,缓冲区的容量不能为负并且不能更改
limit //当写数据到buffer中时,limit一般和capacity相等,当读数据时,limit代表buffer中有效数据的长度。
这些属性总是满足以下条件: 0 <= mark <= position <= limit <= capacity
ByteBuffer的常规方法
ByteBuffer allocate(int capacity) //创建一个指定capacity的ByteBuffer。
ByteBuffer allocateDirect(int capacity) //创建一个direct的ByteBuffer,这样的ByteBuffer在参与IO操作时性能会更好
ByteBuffer wrap(byte [] array)
ByteBuffer wrap(byte [] array, int offset, int length) //把一个byte数组或byte数组的一部分包装成ByteBuffer。
//get put方法不多说
byte get(int index)
ByteBuffer put(byte b)
int getInt() //从ByteBuffer中读出一个int值。
ByteBuffer putInt(int value) // 写入一个int值到ByteBuffer中。
ByteBuffer的特殊方法
Buffer clear() //把position设为0,把limit设为capacity,一般在把数据写入Buffer前调用。
Buffer flip() //把limit设为当前position,把position设为0,一般在从Buffer读出数据前调用。
Buffer rewind() //把position设为0,limit不变,一般在把数据重写入Buffer前调用。和 clear() 类似,只是不改动限制
compact() //将 position 与 limit之间的数据复制到buffer的开始位置,复制后 position = limit -position,limit = capacity, 但如 果position 与limit 之间没有数据的话发,就不会进行复制。
mark() & reset() //通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。
put
写模式下,往buffer里写一个字节,并把postion移动一位。写模式下,一般limit与capacity相等。
flip
写完数据,需要开始读的时候,将postion复位到0,并将limit设为当前postion。
get
从buffer里读一个字节,并把postion移动一位。上限是limit,即写入数据的最后位置。
clear
将position置为0,并不清除buffer内容。
mark & reset
mark相关的方法主要是mark()(标记)和reset()(回到标记).
/**
* 1.txt => VipSoftAAA
*
* @throws Exception
*/
@Test
public void readFile() throws Exception {
FileChannel channel = FileChannel.open(Paths.get("D:\\temp\\1.txt"), StandardOpenOption.READ);
ByteBuffer buf = ByteBuffer.allocate(7);
while (channel.read(buf) != -1) {
buf.flip();
String ss = new String(buf.array());
System.out.println(ss); // 这种方式的读取,不会产生 position 的变化 int idx = 0;
while (buf.hasRemaining()) {
/**
* get() 后 position 会往后+1
* 如果执行完成后,没有 clear(),则新的文件内容不会被读出来。因为 position 已经到了 capacity 值了,没地方放新内容了
*/
System.out.print((char) buf.get());
if (idx == 2) {
/**
* 将 position 与 limit之间的数据复制到buffer的开始位置
* 复制后,position = (pos <= lim ? lim - pos : 0) 接着往后读
* 所以第二次取出AAA时,重置后,position =0 ,这时候会就把buf里面的数据重新再打印一遍变成 AAAAAAtoft
*/
buf.compact(); String compactStr = new String(buf.array());
System.out.println("\r\nCompact => " + compactStr); // compact后一次性读出来看变化,发现Soft被复制到了前面
}
idx++;
}
//第二次读出4个字节,但输出还是AAASoft,说明 clear 只是把 position 设为0, 并没有清除数据
buf.clear();
System.out.println();
}
channel.close();
System.out.println();
}
//输出结果:
//VipSoft //buf.array() 一次性打印全部,无偏移
//Vipoft //compact=7-3=4,所以跳过第三位的S,从第4位开始取值
//AAAtoft //一次性打印全部,因为 compact 将后position~limit的数据复制到了开始位置,所以AAA后面多了个 t
//AAAAAAtoft //第二次 pos>lim 所以 pos=0 把第一次的内容又打了一遍

1.分配内存大小为10的缓存区。索引10的空间是我虚设出来,实际不存在,为了能明显表示capacity。IntBuffer的容量为10,所以capacity为10,在这里指向索引为10的空间。Buffer初始化的时候,limit和capacity指向同一索引。position指向0。

2.往Buffer里加一个数据。position位置移动,capacity不变,limit不变。

3.Buffer读完之后,往bufer里写了5个数据,position指向索引为5的第6个数据,capacity不变,limit不变。

4.执行flip()。这时候对照着,之前flip源码去看。把position的值赋给limit,所以limit=5,然后position=0。capacity不变。结果就是:

5.Buffer开始往外写数据。每写一个,position就下移一个位置,一直移到limit的位置,结束。

上图的顺序就是代码中的IntBuffer从初始化,到读数据,再写数据三个状态下,capacity,position,limit三个属性的变化和关系。
大家可以发现:
1. 0 <= position <= limit <= capacity
2. capacity始终不变
@Test
public void nioTest() {
// 分配内存大小为10的缓存区
IntBuffer buffer = IntBuffer.allocate(10);
System.out.println("capacity:" + buffer.capacity());
for (int i = 0; i < 5; ++i) {
System.out.println("put data position:" + buffer.position());
buffer.put(i+1);
}
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(">>>>>>>> position:" + buffer.position());
System.out.println(">>>>>>>> limit:" + buffer.limit());
System.out.println(">>>>>>>> capacity:" + buffer.capacity());
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(">>>>> flip() 使缓冲区为一系列新的通道写入或相对获取 操作做好准备<<<<<");
buffer.flip();
System.out.println("after flip limit:" + buffer.limit());
System.out.println("enter while loop");
while (buffer.hasRemaining()) {
System.out.println(">>>>>>>> position:" + buffer.position());
System.out.println(">>>>>>>> limit:" + buffer.limit());
System.out.println(">>>>>>>> capacity:" + buffer.capacity());
System.out.println(">>>>>>>> 元素:" + buffer.get());
}
System.out.println(">>>>> rewind() 将位置 position = 0 也就是相当于选取当前缓冲区总的全部有效数据 和clear()类似,只是不改动限制 <<<<<");
buffer.rewind();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(">>>>>>>> position:" + buffer.position());
System.out.println(">>>>>>>> limit:" + buffer.limit());
System.out.println(">>>>>>>> capacity:" + buffer.capacity());
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(">>>>> clear() 用来初始化缓存空间,例如读取文件时将文件内容置入缓存时要先执行此方法 <<<<<");
buffer.clear();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(">>>>>>>> position:" + buffer.position());
System.out.println(">>>>>>>> limit:" + buffer.limit());
System.out.println(">>>>>>>> capacity:" + buffer.capacity());
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}

ByteBuffer 字节缓冲区的更多相关文章
- Java基础知识强化之IO流笔记28:BufferedOutputStream / BufferedInputStream(字节缓冲区流) 之BufferedOutputStream写出数据
1. BufferedOutputStream / BufferedInputStream(字节缓冲区流)的概述 通过定义数组的方式确实比以前一次读取一个字节的方式快很多,所以,看来有一个缓冲区还是非 ...
- 【转】Socket接收字节缓冲区
原创本拉灯 2014年04月16日 10:06:55 标签: socket / 数据包 4448 我们接收Socket字节流数据一般都会定义一个数据包协议( 协议号,长度,内容),由于Socket接收 ...
- Java中的字节流,字符流,字节缓冲区,字符缓冲区复制文件
一:创建方式 1.建立输入(读)对象,并绑定数据源 2.建立输出(写)对象,并绑定目的地 3.将读到的内容遍历出来,然后在通过字符或者字节写入 4.资源访问过后关闭,先创建的后关闭,后创建的先关闭 ...
- Java基础-虚拟内存之映射字节缓冲区(MappedByteBuffer)
Java基础-虚拟内存之映射字节缓冲区(MappedByteBuffer) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.映射字节缓冲区 1>.什么是虚拟内存 答:虚拟内 ...
- Java SocketChannel 读取ByteBuffer字节的处理模型
在JAVA中的流分为字节流或字符流,一般来说采用字符流处理起来更加方便.字节流处理起来相对麻烦,SocketChannel中将数据读取到ByteBuffer中,如何取出完整的一行数据(使用CRLF分隔 ...
- 牛客网Java刷题知识点之字节缓冲区练习之从A处复制文本文件到B处(BufferedReader、BufferedWriter)、复制文本文件的原理图解
不多说,直接上干货! CopyTextByBufTest.java package zhouls.bigdata.DataFeatureSelection.test; import java.io.B ...
- NIO之缓冲区
NIO引入了三个概念: Buffer 缓冲区 Channel 通道 selector 选择器 1.java.io优化建议 操作系统与Java基于流的I/O模型有些不匹配.操作系统要移动的是大块数据(缓 ...
- NIO基础学习——缓冲区
NIO是对I/O处理的进一步抽象,包含了I/O的基础概念.我是基于网上博友的博客和Ron Hitchens写的<JAVA NIO>来学习的. NIO的三大核心内容:缓冲区,通道,选择器. ...
- Java NIO之套接字通道
1.简介 前面一篇文章讲了文件通道,本文继续来说说另一种类型的通道 -- 套接字通道.在展开说明之前,咱们先来聊聊套接字的由来.套接字即 socket,最早由伯克利大学的研究人员开发,所以经常被称为B ...
- java网络编程学习之NIO模型
网上对NIO的解释有很多,但自己一直没有理解,根据自己的理解画出下面这个图,有什么不对的地方,欢迎留言指出. 理解是,客户端先与通过通道Channel与连接(注册到服务器端),然后再传送数据,服务器端 ...
随机推荐
- Hyper-V中的虚拟机(Centos)安装FTP服务
linux上是否装上了ftp服务命令: rpm -qa | grep vsftpd ,若没有安装(无显示版本号)则进行下一步 安装ftp服务,命令: yum -y install ftp vsftpd ...
- 简述几个我们对Redis 7开源社区所做的贡献
Redis 7 已经于2022年4月28号正式发布,其中包括了将近50个新的命令,增加了许多新的特性,并且在整个Redis 6到Redis 7的开发过程中,我也对Redis 的开源社区贡献了一些微薄的 ...
- auto关键词
前言 这个东西在新版本的 C 语言中还是很常用的,尤其是用到 for 循环中.蒟蒻之前也是对这个了解不多,基本没用过(除非迫不得已,因为我的C++编译器太逊了,不支持此操作,用这个操作还得用在线 ID ...
- 腾讯云与树莓派通信遇到的一些问题(树莓派无法ping通腾讯云指定端口)
采用的是socket通信,之前写过C的,这次需要用到python,参考的代码原链接如下:https://www.cnblogs.com/mosu/p/16072146.html. (1)首先开放一个端 ...
- 你知道Spring中BeanFactoryPostProcessors是如何执行的吗?
Spring中的BeanFactoryPostProcessor是在Spring容器实例化Bean之后,初始化之前执行的一个扩展机制.它允许开发者在Bean的实例化和初始化之前对BeanDefinit ...
- 小闫s人格大爆发 | 坐椅子上?study:sleep
hadoop视频 搭建环境 刷单词 看电影
- 教育管理工具类 APP 在智慧校园中的应用探究?
智慧校园是指利用信息化技术,将学校的管理.教育教学等方面进行整合和优化,实现校园运行的智能化和高效化. 在智慧校园建设中,教育管理工具类APP发挥着重要的作用.本文将详细探究教育管理工具类APP在智慧 ...
- Centos、openEuler OS更改源地址
1.配置openEuler软件源仓库 注:以openEuler OS为例,Centos OS相似 vim /etc/yum.repos.d/openEuler.repo 2.常用的源地址 #华为源: ...
- SQL动态化多个分组查询
现有以下三张表,分别为:diqu(地区表),zhiye(职业表),info(信息表) 最基本的分组查询 1 select dqID,jbID,COUNT(1) from info group by d ...
- 项目实战接口开发SpringBoot
目录 一.springboot官方demo开发 二.使用SpringBoot开发get方法接口 三.一个要求携带cookie信息访问的get接口开发 四.需要携带参数的get请求两种开发方式 4.1 ...