在本篇博文中,本人主要讲解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. IDEA编写 Scala时没有语法检查和错误提示

    #问题描述 在IDEA中写scala程序时,一些明显的语法错误不进行检查,没有像写Java时的红线什么的,当编译运行时,编译失败. https://blog.csdn.net/weizheng1000 ...

  2. c++实现单纯形法现行规划问题的求解

    在本程序中默认该现行规划问题有最优解 针对此问题: #include<iostream> using namespace std; int check(float *sigema, int ...

  3. re模块——正则表达式

    import re re.findall('\w','abc123_8()-=') \w:字母数字下划线 \W:非数字字母下划线 \s:空白字符 \S:非空字符 \d:整数数字 \D:非整数数字 \A ...

  4. 【NLP面试QA】激活函数与损失函数

    目录 Sigmoid 函数的优缺点是什么 ReLU的优缺点 什么是交叉熵 为什么分类问题的损失函数为交叉熵而不能是 MSE? 多分类问题中,使用 sigmoid 和 softmax 作为最后一层激活函 ...

  5. Python python 函数参数:必选参数,默认参数

    import math # 函数的必选参数 '''函数的必选参数,指的是函数调用的时候必须传入的参数 ''' def cal (n): return n * n var = cal(2) '''上面的 ...

  6. h5 js数组Array方法总结

    重新复习数组方法. 一.首先说一下构建一个数组. 1.直接定义一个数组. var a = [1,2,3]; 2.通过Array 对象new一个数组,但Array对象根据传参的不同会返回不同的数组对象. ...

  7. python学习要点(一)

    我的个人博客排版更舒服: https://www.luozhiyun.com/archives/264 列表和元组 列表是动态的,长度大小不固定,可以随意地增加.删减或者改变元素(mutable). ...

  8. 以个人身份加入.NET基金会

    .NET 走向开源,MIT许可协议. 微软为了推动.NET开源社区的发展,2014年联合社区成立了.NET基金会. 一年前 .NET 基金会完成第一次全面改选,2014年 .NET基金会的创始成员中有 ...

  9. 最适合新手入门的SpringCloud教程 6—Ribbon负载均衡「F版本」

    SpringCloud版本:Finchley.SR2 SpringBoot版本:2.0.3.RELEASE 源码地址:https://gitee.com/bingqilinpeishenme/Java ...

  10. 如何获取 bing 每日壁纸(超高清版)

    目录 需求描述 实现方式 简单粗暴 如何下载 如何更高清 排坑指南 初级 优点 给有好奇心的孩子 进阶 接口 自动保存 网站集成 爬虫 需求描述 必应作为一个在壁纸圈做搜索引擎最优秀的站点,其每日壁纸 ...