在本篇博文中,本人主要讲解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 抽象类)的更多相关文章

  1. 第7.20节 案例详解:Python抽象类之真实子类

    第7.20节 案例详解:Python抽象类之真实子类 上节介绍了Python抽象基类相关概念,并介绍了抽象基类实现真实子类的步骤和语法,本节结合一个案例进一步详细介绍. 一.    案例说明 本节定义 ...

  2. 详解java基础--抽象类、接口与多态

    抽象类.接口.多态都是面向对象中很基础的东西,我相信看到能本篇博客的人本不会再纠结它的基本定义了,本篇文章将尽量的更加深层次的挖掘其内涵,希望能对大家有帮助. 一.抽象类 1.形式 abstract ...

  3. 详解 NIO流

    在观看本篇博文前,建议先观看本人博文 -- <详解 IO流> NIO流: 首先,本人来介绍下什么是NIO流: 概述: Java NIO ( New IO )是从 Java 1.4 版本开始 ...

  4. Java NIO ———— Buffer 缓冲区详解 入门

    引言缓冲区是一个用于特定基本类型的容器.由java.nio 包定义,所有缓冲区都是 Buffer 抽象类的子类. Java NIO 中的 Buffer ,主要用于与NIO 通道进行交互.数据从通道存入 ...

  5. java NIO Buffer 详解(1)

    1.java.io  最为核心的概念是流(stream),面向流的编程,要么输入流要么输出流,二者不可兼具: 2.java.nio 中拥有3个核心概念: Selector Channel, Buffe ...

  6. Java网络编程和NIO详解4:浅析NIO包中的Buffer、Channel 和 Selector

    Java网络编程与NIO详解4:浅析NIO包中的Buffer.Channel 和 Selector 转自https://www.javadoop.com/post/nio-and-aio 本系列文章首 ...

  7. 【Qt开发】V4L2 API详解 Buffer的准备和数据读取

    前面主要介绍的是:V4L2 的一些设置接口,如亮度,饱和度,曝光时间,帧数,增益,白平衡等.今天看看V4L2 得到数据的几个关键ioctl,Buffer的申请和数据的抓取. 1. 初始化 Memory ...

  8. Protocol Buffer技术详解(数据编码)

    Protocol Buffer技术详解(数据编码) 之前已经发了三篇有关Protocol Buffer的技术博客,其中第一篇介绍了Protocol Buffer的语言规范,而后两篇则分别基于C++和J ...

  9. Protocol Buffer技术详解(Java实例)

    Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...

随机推荐

  1. MySQL5.6 选项和变量整理

    MySQL5.6 选项和变量整理  --allow-suspicious-udfs 这个选项控制是否用户定义函数只有一个xxx符号用于主函数加载.默认,该选项是关闭并且只具有至少一个辅助符号的UDFs ...

  2. 使用Python+OpenCV进行图像处理(二)| 视觉入门

    [前言]图像预处理对于整个图像处理任务来讲特别重要.如果我们没有进行恰当的预处理,无论我们有多么好的数据也很难得到理想的结果. 本篇是视觉入门系列教程的第二篇.整个视觉入门系列内容如下: 理解颜色模型 ...

  3. 万字综述,核心开发者全面解读PyTorch内部机制

    斯坦福大学博士生与 Facebook 人工智能研究所研究工程师 Edward Z. Yang 是 PyTorch 开源项目的核心开发者之一.他在 5 月 14 日的 PyTorch 纽约聚会上做了一个 ...

  4. [HDU2072]单词数<字符串>

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=2072 Problem Description lily的好朋友xiaoou333最近很空,他想了一件没有什 ...

  5. [codevs2597]团伙<并查集>

    题目描述 Description 1920年的芝加哥,出现了一群强盗.如果两个强盗遇上了,那么他们要么是朋友,要么是敌人.而且有一点是肯定的,就是: 我朋友的朋友是我的朋友: 我敌人的敌人也是我的朋友 ...

  6. html第一个程序

    2020-04-05  每日一例第27天 1.打开记事本,输入html格式语言: 2.后台代码注释: <html> <head><!--标题语句--> <ti ...

  7. jmeter实现接口关联的两种方式:正则表达式提取器和json提取器看这篇就够了

    一.前言在开展接口测试或者是接口面试的过程中,我们会发现很多接口需要依赖前面的接口,需要我们动态从前面的接口返回中提取数据,也就是我们通常说的关联. 关联通俗来讲就是把上一次请求的返回内容中的部分截取 ...

  8. D3属性大全

    https://www.cnblogs.com/bester-ace/articles/10948793.html https://www.cnblogs.com/qingmingsang/artic ...

  9. 轻轻松松了解Java的初始化方法(含对象数组的小问题)

    Java基础复习6-初始化 之前讲过了类的一些基础,想必大家都知道了类的定义 什么?你忘了? 下面给你一个例子快速复习 class AClass{ int a; boolean b; void get ...

  10. HTTP Session例子

    HTTP协议是“一次性单向”协议.服务端不能主动连接客户端,只能被动等待并答复客户端请求.客户端连接服务端,发出一个HTTP Request,服务端处理请求,并且返回一个HTTP Response给客 ...