Java之Buffer属性案例详解

一、前言

熟悉NIO的人想必一定不会陌生buffer中position,limit,capacity这三个属性吧,之前在学习的时候遇到一个问题:就是当你先往缓冲区写入一部分数据,然后调用flip()方法,再全部读取完数据,然后再调用flip()方法,此时这三个值的变化是怎样的,研究了一下,决定写下来分享一下。

二、正文

1、介绍

  • position: 它指的是下一次读取或写入的位置。
  • limit: 指定还有多少数据需要写出(在从缓冲区写入通道时),或者还有多少空间可以读入数据(在从通道读入缓冲区时),它初始化是与capacity的值一样,当调用flip()方法之后,它的值会改变成position的值,而position被置0。它箭头所指的位置是最后一位元素的下一位所在的位置*
  • capacity: 指定了可以存储在缓冲区中的最大数据容量,实际上,它指定了底层数组的大小,或者至少是指定了准许我们使用的底层数组的容量,这个初始化后就不会再改变了。

2、图示

以上三个属性值之间有一些相对大小的关系:0 <= position <= limit <= capacity。如果我们创建一个新的容量大小为7的ByteBuffer对象,在初始化的时候,position设置为0,limit和 capacity被设置为7,在以后使用ByteBuffer对象过程中,capacity的值不会再发生变化,而其它两个个将会随着使用而变化。三个属性值分别如图所示:

初始化:

假设我们现在要往这个缓冲区里面写入3个字节,写完之后,position的箭头就会指向3的位置,而limit不变:

此时我们想从缓冲区读取这3个字节,就必须调用flip()方法,调用了flip()方法过后,limit置为position的位置,而position被置为0,也正应证了上面所说的,position它指的是下一次读取或写入的位置,limit它箭头所指的位置是最后一位元素的下一位所在的位置:

现在我们可以调用get()方法,一直从缓冲区里面取数据,直到取完为止,也就是当position与limit的值一样时,就取完了:

这一次简单的读写操作就完成了,如果想恢复成初始状态的话,可以调用clear()方法:

之前学到这里的时候有个疑问,不知道大家想过没有,就是我们在调用了get()方法从缓冲区取完里面的数据,立马去调用flip()方法,那这三个属性的值会是什么变化?如果当我只读了2个字节的数据之后,就不读了,然后再去调用flip(),这三个值又会是怎么变化?其实不管怎么绕,你只要懂得原理,就不难,咱们先看flip()源代码做了什么:

    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

这里不难发现,调用flip()方法,无非就是给这几个变量赋值,将当前的position值赋给limit,然后将position的值置为0,Mark是一个标志变量,咱们以后会提到。熟悉以上代码就不难解决我提出的2个问题:

  • 当你读取完调用flip()的方法      positon:0    limit:3       capacity:7
  • 当你读取2个字节之后调用flip()方法       positon:0    limit:2       capacity:7

这里就解决了我之前遇到的这三个属性值变化的问题!!!

三、测试代码

读取完调用flip:

package com.cing.nio;

import java.io.FileInputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioTest1 {
    public static void main(String[] args) throws Exception{

        FileInputStream fis = new FileInputStream("D:\\A.txt");
        FileChannel fc = fis.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(7);
        output("初始化", buffer);

        fc.read(buffer);
        output("调用READ方法", buffer);

        buffer.flip();
        output("第一次调用flip", buffer);

        while (buffer.remaining() > 0) {
            byte b = buffer.get();
        }
        output("get()", buffer);

        buffer.flip();
        output("第二次flip", buffer);

        fis.close();
    }

    public static void output(String step, Buffer buffer) {
        System.out.println(step + " : ");
        System.out.println("buffer: " + buffer + ", ");
    }
}

输出结果为:

初始化 :
buffer: java.nio.HeapByteBuffer[pos=0 lim=7 cap=7],
调用READ方法 :
buffer: java.nio.HeapByteBuffer[pos=3 lim=7 cap=7],
第一次调用flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=3 cap=7],
get() :
buffer: java.nio.HeapByteBuffer[pos=3 lim=3 cap=7],
第二次flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=3 cap=7], 

读取2字节之后调用flip:

package com.cing.nio;

import java.io.FileInputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioTest1 {
    public static void main(String[] args) throws Exception{

        FileInputStream fis = new FileInputStream("D:\\A.txt");
        FileChannel fc = fis.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(7);
        output("初始化", buffer);

        fc.read(buffer);
        output("调用READ方法", buffer);

        buffer.flip();
        output("第一次调用flip", buffer);

        while (buffer.remaining() > 1) {
            byte b = buffer.get();
        }
        output("get()", buffer);

        buffer.flip();
        output("第二次flip", buffer);

        fis.close();
    }

    public static void output(String step, Buffer buffer) {
        System.out.println(step + " : ");
        System.out.println("buffer: " + buffer + ", ");
    }
}
 

输出结果为:

初始化 :
buffer: java.nio.HeapByteBuffer[pos=0 lim=7 cap=7],
调用READ方法 :
buffer: java.nio.HeapByteBuffer[pos=3 lim=7 cap=7],
第一次调用flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=3 cap=7],
get() :
buffer: java.nio.HeapByteBuffer[pos=2 lim=3 cap=7],
第二次flip :
buffer: java.nio.HeapByteBuffer[pos=0 lim=2 cap=7], 

NIO的知识学起来还是比较有趣的,期待下次与大家分享这块的知识!

到此这篇关于Java之Buffer属性案例详解的文章就介绍到这了,更多相关Java之Buffer属性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一文学习Java NIO的ByteBuffer工作原理

    网络数据的基本单位永远是 byte(字节).Java NIO 提供 ByteBuffer 作为字节的容器,但该类过于复杂,有点难用. ByteBuf是Netty当中的最重要的工具类,它与JDK的ByteBuffer原理基本上相同,也分为堆内与堆外俩种类型,但是ByteBuf做了极大的优化,具有更简单的API,更多的工具方法和优秀的内存池设计. 1 API Netty 的数据处理 API 通过两个组件暴露--抽象类ByteBuf 和 接口 ByteBufHolder. ByteBuf API 的优

  • java基础之NIO介绍及使用

    一.NIO java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络. 二.三大组件 NIO三大组件:Channel.Buffer.Selector 1.Channel 和Buffer Channel是一个对象,可以通过它读取和写入数据.拿 NIO 与原来的 I/O 做个比较,通道就像是流,而且他们面向缓冲区(Buffer)的

  • 又又叕出BUG啦!理智分析Java NIO的ByteBuffer到底有多难用

    一.前言 ByteBuf是Netty当中的最重要的工具类,它与JDK的ByteBuffer原理基本上相同,也分为堆内与堆外俩种类型,但是ByteBuf做了极大的优化,具有更简单的API,更多的工具方法和优秀的内存池设计. 二.API Netty 的数据处理 API 通过两个组件暴露--抽象类ByteBuf 和 接口 ByteBufHolder. ByteBuf API 的优点: 它可以被用户自定义的缓冲区类型扩展 通过内置的复合缓冲区类型实现了透明的零拷贝: 容量可以按需增长(类似于 JDK 的

  • Java非阻塞I/O模型之NIO相关知识总结

    组件说明 (1)Channel:NIO模型中的管道,管道是链接建立和通信的重要组件,我们可以理解管道是一个容器环境,我们所有的I/O的建立读取都可以在这个容器中进行 (2)Selector:NIO中的选择器,NIO是由事件驱动的,当有链接事件或者读取事件发生时,这个事件可以注册到这个选择器上,并且最终被我们检测到. (3)SelectionKey:我们可以在Selector中进行检测是否有SelectionKey产生,并且根据这个SelectionKey中的信息判断时什么事件发生了. 代码说明

  • 小白也可以学会的Java NIO的Write事件

    一.NIO Server端 1.1 多路复用开发一般步骤 //打开选择器 Selector selector = Selector.open(); //打开通到 ServerSocketChannel socketChannel = ServerSocketChannel.open(); //配置非阻塞模型 socketChannel.configureBlocking(false); //绑定端口 socketChannel.bind(new InetSocketAddress(8080));

  • 浅谈Java中IO和NIO的本质和区别

    IO的本质 IO的作用就是从外部系统读取数据到java程序中,或者把java程序中输出的数据写回到外部系统.这里的外部系统可能是磁盘,网络流等等. 因为对所有的外部数据的处理都是由操作系统内核来实现的,对于java应用程序来说,只是调用操作系统中相应的接口方法,从而和外部数据进行交互. 所有IO的本质就是对Buffer的处理,我们把数据放入Buffer供系统写入外部数据,或者从系统Buffer中读取从外部系统中读取的数据.如下图所示: 用户空间也就是我们自己的java程序有一个Buffer,系统

  • java Nio使用NioSocket客户端与服务端交互实现方式

    NioSocket 客户端与服务端交互实现 java Nio是jdk1.4新增的io方式-–nio(new IO),这种方式在目前来说算不算new,更合适的解释应该是non-block IO. non-block是相对于传统的io方式来讲的.传统的Io方式是阻塞的,我们拿网络io来举例,传统的io模型如下: 服务端主线程负责不断地server.accept(),如果没有客户端请求主线程就会阻塞,当客户端请求时,主线程会通过线程池创建一个新的线程执行. 简单解释就是一个线程负责一个客户端的sock

  • Java实现NIO聊天室的示例代码(群聊+私聊)

    功能介绍 功能:群聊+私发+上线提醒+下线提醒+查询在线用户 文件 Utils 需要用maven导入下面两个包 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <dependency> <group

  • Java之Buffer属性案例详解

    一.前言 熟悉NIO的人想必一定不会陌生buffer中position,limit,capacity这三个属性吧,之前在学习的时候遇到一个问题:就是当你先往缓冲区写入一部分数据,然后调用flip()方法,再全部读取完数据,然后再调用flip()方法,此时这三个值的变化是怎样的,研究了一下,决定写下来分享一下. 二.正文 1.介绍 position: 它指的是下一次读取或写入的位置. limit: 指定还有多少数据需要写出(在从缓冲区写入通道时),或者还有多少空间可以读入数据(在从通道读入缓冲区时

  • Java NIO Buffer实现原理详解

    目录 1.Buffer的继承体系 2.Buffer的操作API使用案例 3.Buffer的基本原理 4.allocate方法初始化一个指定容量大小的缓冲区 5.slice方法缓冲区分片 6.只读缓冲区 7.直接缓冲区 8.内存映射 1.Buffer的继承体系 如上图所示,对于Java中的所有基本类型,都会有一个具体的Buffer类型与之对应,一般我们最经常使用的是ByteBuffer. 2.Buffer的操作API使用案例 举一个IntBuffer的使用案例: /** * @author csp

  • Java之Jackson使用案例详解

    序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象. Json是什么? Jason是 JavaScript Object Notation-  JavaScript对象表示法,是一种轻量级数据交换格式.主要用于数据传输,比如说在后端写了一个Java对象,想在其他地方(前端)使用这个对象,就需要转换为Json这种形式进行传输. 1.

  • Java反射 PropertyDescriptor类案例详解

    JAVA中反射机制(JavaBean的内省与BeanUtils库) 内省(Introspector) 是Java 语言对JavaBean类属性.事件的一种缺省处理方法. JavaBean是一种特殊的类,主要用于传递数据信息,这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则.如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为"值对象"(Value Object),或"VO".方法比较少.这些信息储存在类的私有变量中,通过set(

  • Java Springboot websocket使用案例详解

    什么是WebSocket WebSocket是一种在单个TCP连接上进行全双工通信的协议 - 为什么要实现握手监控管理 如果说,连接随意创建,不管的话,会存在错误,broken pipe 表面看单纯报错,并没什么功能缺陷等,但实际,请求数增加,容易导致系统奔溃.这边画重点. 出现原因有很多种,目前我这边出现的原因,是因为客户端已关闭连接,服务端还持续推送导致. 如何使用 下面将使用springboot集成的webSocket 导入Maven 首先SpringBoot版本 <parent> &l

  • Java并发之Condition案例详解

    目录 一.Condition接口介绍和示例 二.Condition接口常用方法 三.Condition接口原理简单解析 3.1.等待 3.2.通知 四.总结 五.利用Condition实现生产者消费者模式 在使用Lock之前,我们使用的最多的同步方式应该是synchronized关键字来实现同步方式了.配合Object的wait().notify()系列方法可以实现等待/通知模式.Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方

  • Java之JSF框架案例详解

    这是一个分为两部分的系列,其中我介绍了JSF 2及其如何适合Java EE生态系统. 在第1部分中,我将介绍JavaServer Pages(JSF)背后的基本思想 ,在第2部分中,将介绍Facelets声明语言 . 在构建Web应用程序时,我们为最终用户提供了一种与我们的应用程序进行交互的方式,这就是JSF所提供的. 我将向您介绍MVC设计模式以及如何使用它,并且您将发现Facelets视图语言及其使用方式,如何将数据和事件绑定到上下文以及如何通过表达语言来实现. 我将通过查看替代模板框架(例

  • Java SpringBoot Validation用法案例详解

    目录 constraints分类 对象集成constraints示例 SpringBoot集成自动验证 集成maven依赖 验证RequestBody.Form对象参数 验证简单参数 验证指定分组 全局controller验证异常处理 自定义constraints @DateFormat @PhoneNo 使用自定义constraint注解 问题 提到输入参数的基本验证(非空.长度.大小.格式-),在以前我们还是通过手写代码,各种if.else.StringUtils.isEmpty.Colle

  • Java Spring拦截器案例详解

    springmvc提供了拦截器,类似于过滤器,他将在我们的请求具体出来之前先做检查,有权决定接下来是否继续,对我们的请求进行加工. 拦截器,可以设计多个. 通过实现handlerunterceptor,这是个接口 定义了非常重要的三个方法: 后置处理 前置处理 完成处理 案例一: 通过拦截器实现方法耗时统计与警告 package com.xy.interceptors; import org.springframework.web.servlet.HandlerInterceptor; impo

  • Java Thread之Sleep()案例详解

    一.API简介 Thread.sleep()是Thread类的一个静态方法,使当前线程休眠,进入阻塞状态(暂停执行),如果线程在睡眠状态被中断,将会抛出IterruptedException中断异常..主要方法如下: [a]sleep(long millis)  线程睡眠 millis 毫秒 [b]sleep(long millis, int nanos)  线程睡眠 millis 毫秒 + nanos 纳秒 Api文档: 二.使用方法 注意:在哪个线程里面调用sleep()方法就阻塞哪个线程.

随机推荐