Java NIO之Buffer的使用

Wesley13
• 阅读 599

Java NIO之Buffer的使用

作者 | bmilk

来源 | cnblogs.com/bmilk/p/13225817.html

Buffer简介

缓冲区(Buffer):本质上是一个数组,用于临时保存、写入以及读取数据。在Java NIO中,
该内存块包含在NIO Buffer对象当中,NIO Buffer对象还提供了一组接口来访问该内存块。

根据数据类型的不同,Java为除了boolean类型之外的其余7种基本类型提供了相应类型的缓冲区,
分别是ByteBufferCharBufferShortBufferIntBufferLongBuffer
FloatBufferDoubleBuffer。他们都继承自抽象类Buffer类,他们的管理方式也都几乎一样。
UML类图如下:
Java NIO之Buffer的使用

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的使用一般分为四个步骤

  1. Buffer中写入数据

  2. Buffer切换为读取模式

  3. 读取Buffer

  4. 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有两种方式:

  1. Buffer种读取数据到Channel

  2. 使用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()

其他的一些方法

  1. 使用rewind()Buffer重复读取数

  2.  //使用`rewind()`从`Buffer`重复读取数据//Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。//limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。Buffer rewind = byteBuffer.rewind();
    
  3. compact()方法

clear()会使使各个指针恢复初始位置,但是实际中可能存在部分数据还没有被使用,而后续需要使用。
又必须清理一部分Buffer的空间,compact()方法会将所有未读数据拷贝到Buffer的起始处,
然后将position指针设置到最后一个未读元素的后面,现在Buffer可以进行写数据,
但是不会覆盖前面的未读的数据。

  1. 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相等

  • 有相同的类型(bytecharint等)

  • Buffer中剩余的bytechar等的个数相等。

  • ‘Buffer‘中所有剩余的‘byte‘、‘char‘等都相同 ‘ B u f f e r ‘ 中 所 有 剩 余 的 ‘ b y t e ‘ 、 ‘ c h a r ‘ 等 都 相 同

compareTo()方法比较两个两个Buffer的大小,仅比较剩余元素(bytechar等)
如果满足下列条件,则认为一个Buffer“小于”另一个Buffer

  • 第一个不相等的元素小于另一个Buffer中对应的元素

  • 所有元素都相等,但第一个Buffer比另一个先耗尽(第一个Buffer的元素个数比另一个少)。

直接缓冲区与非直接缓冲区

  • 非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM内存中

  • 直接俄缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以在某些情况下提高效率

非直接缓冲区

  • 非直接缓冲区数据流向图
    Java NIO之Buffer的使用

直接缓冲区

  • 直接缓冲区数据流向图
    Java NIO之Buffer的使用

直接缓冲区(物理内存映射文件):相比非直接缓冲区省略了copy的过程,所以说直接缓区可以一定程度上提高效率

弊端:

  • 开辟空间时资源消耗大

  • 不安全,java程序将数据写入物理内存映射文件中,之后数据将不受Java程序控制,
    什么时候写入硬盘无法控制(由操作系统控制),当垃圾回收机制释放引用后才能断开与之的连接

小结

  • 缓冲区要么是直接的,要么是非直接的如果为直接字节缓冲区,则java虚拟机会见最大努力直接在此缓冲区上执行本机I/O
    也就是说,每次调用基础操作系统的I/O之前或之后,虚拟机都回尽量避免将缓冲区的内容复制到中间缓冲区或者从中间缓冲区中复制内容。

  • 直接字节缓冲区可以通过调用此类的allocateDirect()工厂方法来创建,
    此方法返回的缓冲区进行分配和取消分配所需的程本通常高于非直接缓冲区,
    直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此他们对应用程序内存需求造成的影响可能并不明显,
    所以建议直接缓冲区主要分配给易受基础系统的本机I/O操作影响的大型、持久得缓冲区。
    一般情况下,最好尽在直接缓冲区能在程序性能方面带来明显好处时分配他们。

  • 直接字节缓冲区还可以通过FileChannelmap()方法,将文件区域直接映射到内存中来创建,
    该方法返回MappedByteBufferJava的实现有助于JNI从本地及代码创建直接字节缓冲区,
    如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域。
    则试图访问该区域不会更改缓冲区的内容,并且将会在访问期间或稍后的时间导致抛出不确定的异常

  • 字节缓冲区是直接缓冲区还是非直接缓冲区可以通过调用其isDirect()方法来确定,提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。

总结

本文简单介绍了Buffer的种类,并对常用方法进行乐简单的介绍

参考资料

Java NIO系列教程(三) Buffer

Java NIO之Buffer的使用

Java NIO之Buffer的使用

     

感谢点赞支持下哈 Java NIO之Buffer的使用

本文分享自微信公众号 - java1234(gh_27ed55ecb177)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
8个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这