详解 缓冲区(Buffer 抽象类)
在本篇博文中,本人主要讲解NIO 的两个核心点 —— 缓冲区(Buffer) 和 通道 (Channel)之一的 缓冲区(Buffer),
有关NIO流的其他知识点请观看本人博文《详解 NIO流》
缓冲区(Buffer 类):
简介:
缓冲区( Buffer ):
一个用于特定基本数据类型的容器。
由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,
数据是从通道读入缓冲区,从缓冲区写入通道中的
Buffer 就像一个数组,可以保存多个相同类型的数据
那么,现在,本人来介绍下Buffer 抽象类的 常用子类:
ByteBuffer —— 存储byte类型的缓冲区
CharBuffer —— 存储char类型的缓冲区
ShortBuffer —— 存储short类型的缓冲区
IntBuffer —— 存储int类型的缓冲区
LongBuffer —— 存储long类型的缓冲区
FloatBuffer —— 存储float类型的缓冲区
DoubleBuffer —— 存储double类型的缓冲区
上述 Buffer 类 他们都采用相似的方法进行管理数据,
只是各自管理的数据类型不同而已。
Buffer是没有构造方法的,
Buffer的对象都是通过 allocate(int capacity)方法 来获取的
现在,本人来讲解下如何获取 Buffer 抽象类 的 子类的对象:
手段1:
直接缓冲区:子类的allocateDirect()静态方法
非直接缓冲区:子类的allocate()静态方法
手段2:
直接缓冲区:通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。(该方法返回MappedByteBuffer )
那么,本人现在来展示下处理缓冲区的核心API:
核心API:
put() 存入数据到缓冲区中
get() 获取缓冲区中的数据
flip() 切换读取数据模式
rewind() 可重复读
clear() 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
mark() 标记是一个索引,方便后面使用reset()方法跳回这标记处
reset() 使position恢复到之前用mark()方法标记的
现在,本人来讲解下Buffer的基本属性:
Buffer的基本属性:
Buffer 中的重要属性:
容量 (capacity) :
表示 Buffer 最大数据容量。
缓冲区容量不能为负,并且创建后不能更改。
限制 (limit) :
第一个不应该读取或写入的数据的索引,即位于 limit 后的数据不可读写。
缓冲区的限制不能为负,并且不能大于其容量。
位置 (position) :
下一个要读取或写入的数据的索引。
缓冲区的位置不能为负,并且不能大于其限制
标记 (mark) 与重置 (reset) :
标记是一个索引,
通过 Buffer 中的 mark() 方法 指定 Buffer 中一个特定的 position ,之后可以通过调用 reset() 方法恢复到这个 position.
标记、位置、限制、容量遵守以下不变式:
下图展示了处理缓冲区的API 会对缓冲区的属性的影响:
现在,本人来展示下Buffer类的API:
Buffer的API:
方法 描述
Buffer clear() 清空缓冲区并返回对缓冲区的引用
Buffer flip() 将缓冲区的界限设置为当前位置,并将当前位置充值为0
int capacity() 返回Buffer的capacity大小
boolean hasRemaining() 判断缓冲区中是否还有元素
int limit() 返回Buffer的界限(limit)的位置
Buffer limit(intn) 将设置缓冲区界限为n,并返回一个具有新limit的缓冲区对象
Buffer mark() 对缓冲区设置标记
int position() 返回缓冲区的当前位置position
Buffer position(int n) 将设置缓冲区的当前位置为n,并返回修改后的Buffer对象
int remaining() 返回position和limit之间的元素个数
Buffer reset() 将位置position转到以前设置的mark所在的位置
Buffer rewind() 将位置设为为0,取消设置的mark
那么,现在,本人来通过一个例子来展示下这些API 的使用:
package edu.youzg.about_nio.core;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
String str="youzhuange";
//申请10字节缓冲区空间
System.out.println("--------------allocate(10)-------");
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
int capacity = byteBuffer.capacity();
int position = byteBuffer.position();
int limit = byteBuffer.limit();
System.out.println("capacity:"+capacity);
System.out.println("position:"+position);
System.out.println("limit:"+limit);
//往容器中放数据 put();
System.out.println("----------put()-----------");
byteBuffer.put(str.getBytes());
capacity = byteBuffer.capacity();
position = byteBuffer.position();
limit = byteBuffer.limit();
System.out.println("capacity:" + capacity);
System.out.println("position:" + position);
System.out.println("limit:" + limit);
//读取缓冲区中的数据,切换成读取模式
System.out.println("---------flip()--------");
byteBuffer.flip();
capacity = byteBuffer.capacity();
position = byteBuffer.position();
limit = byteBuffer.limit();
System.out.println("capacity:" + capacity);
System.out.println("position:" + position);
System.out.println("limit:" + limit);
//读取数据 get()
System.out.println("---------get()---------");
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
System.out.println(new String(bytes, 0, byteBuffer.limit()));
capacity = byteBuffer.capacity();
position = byteBuffer.position();
limit = byteBuffer.limit();
System.out.println("capacity:" + capacity);
System.out.println("position:" + position);
System.out.println("limit:" + limit);
//可重复读取
System.out.println("---------rewind()---------");
byteBuffer.rewind();
capacity = byteBuffer.capacity();
position = byteBuffer.position();
limit = byteBuffer.limit();
System.out.println("capacity:" + capacity);
System.out.println("position:" + position);
System.out.println("limit:" + limit);
//标记 mark
System.out.println("---------mark()--------");
byteBuffer.mark();
byteBuffer.get(bytes,2,2);
System.out.println(byteBuffer.position());
//回到上一次标记position的位置 使用reset();就可以回到上一次标记的位置
System.out.println("---------reset()--------");
byteBuffer.reset();
System.out.println(byteBuffer.position());
//查询还有没有可读数据
System.out.println("---------remaining()--------");
if(byteBuffer.hasRemaining()){
System.out.println(byteBuffer.remaining()); //remaining()还有多少可读取数据
}
//清空缓冲区
System.out.println("--------clear()----------------");
//clear()并不是把缓冲区里面的字节数据清掉,而是把这些指针,设置到初始状态
byteBuffer.clear();
capacity = byteBuffer.capacity();
position = byteBuffer.position();
limit = byteBuffer.limit();
System.out.println("capacity:" + capacity);
System.out.println("position:" + position);
System.out.println("limit:" + limit);
byte b = byteBuffer.get();
System.out.println((char)b);
}
}
本人再来展示下运行结果:
那么,现在,本人来讲解缓冲区的分类 —— 直接缓冲区 与 非直接缓冲区:
字节缓冲区要么是直接的,要么是非直接的。
直接缓冲区是将 缓冲区建立在 物理内存中,非直接缓冲区是将缓冲区建立在JVM中
如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。
也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),
虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
直接字节缓冲区可以通过调用此类的 allocateDirect() 工厂方法 来创建。
此方法返回的 缓冲区进行分配和取消分配所需成本通常高于 非直接缓冲区 。
直接缓冲区的内容 可以驻留在常规的垃圾回收堆之外,
因此,它们对应用程序的内存需求量造成的影响可能并不明显。
所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。
一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。
该方法返回MappedByteBuffer 。
Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。
如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,
则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。
字节缓冲区是直接缓冲区还是非直接缓冲区 可通过调用其 isDirect() 方法来确定。
提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。
现在,本人来通过两张图来展示下这两种缓冲区的底层传输步骤:
至于这两种缓冲区的使用,请观看本人博文 —— 《详解 通道 (Channel 接口)》
现在,本人对上图中的内存映射文件为什么高效率做下解释:
内存映射文件效率为什么高:
文件I/O的读操作,
会先向文件设备发起读请求,驱动把请求要读的数据读取到文件的缓冲区中(这个缓冲区位于内核),
然后再把这个缓冲区中的数据复制到程序虚拟地址空间中的一块区域中。
文件I/O的写操作,
会向文件设备发起写请求,驱动把要写入的数据复制到程序的缓冲区中(位于用户空间),
然后再把这个缓冲区的数据复制到文件的缓冲区中。
内存映射文件,是把位于硬盘中的文件看做是程序地址空间中一块区域对应的物理存储器,
文件的数据就是这块区域内存中对应的数据,读写文件中的数据,直接对这块区域的地址操作 就可以,
减少了内存复制的环节。
所以说,内存映射文件比起文件I/O操作,效率要高,而且文件越大,体现出来的差距越大。
(本人 NIO流 博文链接:https:////www.cnblogs.com/codderYouzg/p/12418765.html)
详解 缓冲区(Buffer 抽象类)的更多相关文章
- 第7.20节 案例详解:Python抽象类之真实子类
第7.20节 案例详解:Python抽象类之真实子类 上节介绍了Python抽象基类相关概念,并介绍了抽象基类实现真实子类的步骤和语法,本节结合一个案例进一步详细介绍. 一. 案例说明 本节定义 ...
- 详解java基础--抽象类、接口与多态
抽象类.接口.多态都是面向对象中很基础的东西,我相信看到能本篇博客的人本不会再纠结它的基本定义了,本篇文章将尽量的更加深层次的挖掘其内涵,希望能对大家有帮助. 一.抽象类 1.形式 abstract ...
- 详解 NIO流
在观看本篇博文前,建议先观看本人博文 -- <详解 IO流> NIO流: 首先,本人来介绍下什么是NIO流: 概述: Java NIO ( New IO )是从 Java 1.4 版本开始 ...
- Java NIO ———— Buffer 缓冲区详解 入门
引言缓冲区是一个用于特定基本类型的容器.由java.nio 包定义,所有缓冲区都是 Buffer 抽象类的子类. Java NIO 中的 Buffer ,主要用于与NIO 通道进行交互.数据从通道存入 ...
- java NIO Buffer 详解(1)
1.java.io 最为核心的概念是流(stream),面向流的编程,要么输入流要么输出流,二者不可兼具: 2.java.nio 中拥有3个核心概念: Selector Channel, Buffe ...
- Java网络编程和NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector
Java网络编程与NIO详解4:浅析NIO包中的Buffer.Channel 和 Selector 转自https://www.javadoop.com/post/nio-and-aio 本系列文章首 ...
- 【Qt开发】V4L2 API详解 Buffer的准备和数据读取
前面主要介绍的是:V4L2 的一些设置接口,如亮度,饱和度,曝光时间,帧数,增益,白平衡等.今天看看V4L2 得到数据的几个关键ioctl,Buffer的申请和数据的抓取. 1. 初始化 Memory ...
- Protocol Buffer技术详解(数据编码)
Protocol Buffer技术详解(数据编码) 之前已经发了三篇有关Protocol Buffer的技术博客,其中第一篇介绍了Protocol Buffer的语言规范,而后两篇则分别基于C++和J ...
- Protocol Buffer技术详解(Java实例)
Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...
随机推荐
- 《JavaScript 模式》读书笔记(5)— 对象创建模式2
这一篇,我们主要来学习一下私有属性和方法以及模块模式. 三.私有属性和方法 JavaScript并没有特殊的语法来表示私有.保护.或公共属性和方法,在这一点上与Java或其他语言是不同的.JavaSc ...
- matplotlib.pyplot.text
matplotlib.pyplot.text(x, y, s, fontdict=None, withdash=<deprecated parameter>, **kwargs)[sour ...
- OpenCV-Python 立体图像的深度图 | 五十二
目标 在本节中, 我们将学习根据立体图像创建深度图. 基础 在上一节中,我们看到了对极约束和其他相关术语等基本概念.我们还看到,如果我们有两个场景相同的图像,则可以通过直观的方式从中获取深度信息.下面 ...
- 感知器基础原理及python实现
简单版本,按照李航的<统计学习方法>的思路编写 数据采用了著名的sklearn自带的iries数据,最优化求解采用了SGD算法. 预处理增加了标准化操作. ''' perceptron c ...
- iOS Hook
HOOK 译为"钩子"或挂钩.在 iOS 逆向中指改变程序运行流程的一种技术. iOS 中 hook 技术的几种方式 Method Swizzle 利用 OC 的 Runtime ...
- 15.自动部署web工程
用maven自动部署web工程 在pom.xml中写入以下: <build> <!--最终名称,进入网页时有http://localhost:8080/xxx/--> < ...
- Dome_iptest_server
一个简单的ip测试服务器 打印返回 请求头request import socket def send_data(conn, data=None, status=200): if status != ...
- python数据分析工具 | matplotlib
不论是数据挖掘还是数学建模,都免不了数据可视化的问题.对于 Python 来说,matplotlib 是最著名的绘图库,它主要用于二维绘图,当然也可以进行简单的三维绘图.它不但提供了一整套和 Matl ...
- 多线程学习笔记(五)---- 在JDK文档的使用
1.前言 我们经常在JDK文档中见到一些类上的介绍说,该类是"安全的"."不安全"."效率高"."效率低"的词眼.这里, ...
- 为什么Swift和Python要抛弃++\--?
简单好用的++.-- 说到自增(++)\自减(--)运算符,小伙伴们应该都不会陌生,在很多编程语言的代码中,都经常出现它们的身影. 比如常用的for语句 for (int i = 0; i < ...