buffer java nio_Java NIO深入理解Buffer(缓冲区)

前言

Github:https://github.com/yihonglei/java-all

Project:java-nio

一 Buffer概述

Java NIO中的Buffer用于和NIO通道进行交互。数据是从通道读入缓冲区,从缓冲区写入到通道中的。

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,

并提供了一组方法,用来方便的访问该块内存。

二 Buffer重要知识点分析

1、Buffer基本用法

使用Buffer读写数据一般遵循以下四个步骤:

1)写入数据到Buffer,一般有可以从Channel读取到到缓冲区中,也可以调用put方法写入。

2)调用flip()方法,切换数据模式。

3)从Buffer中读取数据,一般从缓冲区读取数据写入到通道中,也可以调用get方法读取。

4)调用clear()方法或者compact()方法清空缓冲区。

当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。

在读模式下,可以读取之前写入到buffer的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。

有两种方式能清空缓冲区:

1)clear()方法会清空整个缓冲区。

2)compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区

未读数据的后面。

package com.lanhuigu.nio.buffer;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.FileChannel;

/**

* 使用Buffer读写数据一般遵循以下四个步骤:

* (1)写入数据到Buffer,一般有可以从Channel中读取到缓冲区,也可以调用put方法写入。

* (2)调用flip()方法,切换数据模式。

* (3)从Buffer中读取数据

* (4)调用clear()方法或者compact()方法

* 当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。

* 在读模式下,可以读取之前写入到buffer的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。

*

* 有两种方式能清空缓冲区:

* 1)clear()方法会清空整个缓冲区。

* 2)compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

*/

public class BufferRWTest {

/**

* 文件复制实例

*/

public static void main(String[] args) {

// 源文件

File fromFile = new File("C:\\mycode\\hello.txt");

// 目标文件

File toFile = new File("C:\\mycode\\hello-copy.txt");

try (

// 根据源文件创建文件输入流

FileInputStream fis = new FileInputStream(fromFile);

// 根据目标文件创建文件输出流,如果文件不存在,自动创建

FileOutputStream fos = new FileOutputStream(toFile);

// 1. 获取通道

FileChannel inChannel = fis.getChannel();

FileChannel outChannel = fos.getChannel();

) {

// 2. 分配指定大小的缓冲区

ByteBuffer buffer = ByteBuffer.allocate(48);

// 3. 将通道中的数据读取到缓冲区

while (inChannel.read(buffer) != -1) {

// 切换成读数据模式

buffer.flip();

// 4. 从缓冲区读取数据写入到通道中

outChannel.write(buffer);

// 清空缓冲区

buffer.clear();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

2、Buffer的capacity、position和limit

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,

并提供了一组方法,用来方便的访问该块内存。为了理解Buffer的工作原理,需要熟悉它的三个属性:

.capacity

.position

.limit

position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,capacity的含义总是一样的。

这里有一个关于capacity,position和limit在读写模式中的说明:

42705e543d9f7d7683812dc13b5c2681.png

capacity

作为一个内存块,Buffer有一个固定的大小值,也叫"capacity"。你只能往里写capacity个byte、long,char等类型。

一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。

position

当你写数据到Buffer中时,position表示当前的位置。初始的position值为0。

当一个byte、long等数据写到Buffer后,position会向前移动到下一个可插入数据的Buffer单元。

position最大可为capacity –1当读取数据时,也是从某个特定位置读。

当将Buffer从写模式切换到读模式,position会被重置为0。当从Buffer的position处读取数据时,

position向前移动到下一个可读的位置。

limit

在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。写模式下,limit等于Buffer的capacity。

当切换Buffer到读模式时,limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,

limit会被设置成写模式下的position值。

换句话说,你能读到之前写入的所有数据,即limit被设置成已写数据的数量,这个值在写模式下就是position。

3、Buffer的类型

如下是Java NIO中最重要的缓冲区的实现:

ByteBuffer

CharBuffer

DoubleBuffer

FloatBuffer

IntBuffer

LongBuffer

ShortBuffer

MappedByteBuffer

这些Buffer覆盖了你能通过IO发送的基本数据类型:byte, short, int, long, float, double和char。

4、Buffer的分配

要想获得一个Buffer对象首先要进行分配。每一个Buffer类都有一个allocate方法。

分配48字节capacity的ByteBuffer的例子:

ByteBuffer buf = ByteBuffer.allocate(48);

分配一个可存储1024个字符的CharBuffer:

CharBuffer buf = CharBuffer.allocate(1024);

5、向Buffer中写入数据

写数据到Buffer有两种方式:

1)从Channel写到Buffer。

2)通过Buffer的put()方法写到Buffer里。

从Channel写到Buffer的例子:

int bytesRead = inChannel.read(buf); // 从Channel(通道)读取到Buffer(缓冲区)中

通过put方法写Buffer的例子:

buf.put(127);

put方法有很多版本,允许你以不同的方式把数据写入到Buffer中。

例如,写到一个指定的位置,或者把一个字节数组写入到Buffer。

6、flip()方法使用

flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。

换句话说,position现在用于标记读的位置,limit表示之前写进了多少个byte、char等现在能读取多少个byte、char等。

7、从Buffer中读取数据

从Buffer中读取数据有两种方式:

1)从Buffer读取数据到Channel。

2)使用get()方法从Buffer中读取数据。

从Buffer读取数据到Channel的例子:

// 从Buffer中读取数据写入到通道中

int bytesWritten = inChannel.write(buf);

使用get()方法从Buffer中读取数据的例子:

byte aByte = buf.get();

get方法有很多版本,允许你以不同的方式从Buffer中读取数据。

例如,从指定position读取,或者从Buffer中读取数据到字节数组。

8、rewind()方法

Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。

limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。

9、clear()和compact()方法

一旦读完Buffer中的数据,需要让Buffer准备好再次被写入。可以通过clear()或compact()方法来完成。

如果调用的是clear()方法,position将被设回0,limit被设置成capacity的值。换句话说,Buffer被清空了。

Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。如果Buffer中有一些未读的数据,

调用clear()方法,数据将“被遗忘”,意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。

如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先写些数据,那么使用compact()方法。

compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。

limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。

10、mark()和reset()方法

通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。

之后可以通过调用Buffer.reset()方法恢复到这个position。例如:

buffer.mark();

//调用buffer读写方法,下面通过调用reset恢复到调用前的position位置。

buffer.reset(); // set position back to mark

11、equals()和compareTo()方法

可以使用equals()和compareTo()方法两个Buffer。

equals()

当满足下列条件时,表示两个Buffer相等。

1)有相同的类型(byte、char、int等)。

2)Buffer中剩余的byte、char等的个数相等。

3)Buffer中所有剩余的byte、char等都相同。

如你所见,equals只是比较Buffer的一部分,不是每一个在它里面的元素都比较。

实际上,它只比较Buffer中的剩余元素。

compareTo()

compareTo()方法比较两个Buffer的剩余元素(byte、char等),如果满足下列条件,则认为一个Buffer"小于"另一个Buffer。

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/442405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java robot键值_Java:使用Robot类模拟键盘, 以Alt码方式输出汉字

java.awt.Robot类Java提供java.awt.Robot类来模拟操作键盘和鼠标, 下面是一个简单的demopublic static void keyPressByInt(Robot r,int key, int time){r.keyPress(key);r.keyRelease(key);if (time > 0) {r.delay(time);}}public static void main(String[] args) throws …

php设置上传文件大小限制_php修改上传文件大小限制实例详解

php修改上传文件大小限制实例详解php在默认情况下,只允许上传一定大小的文件,当上传文件大小超过这个值时,将会出错。本文章将向大家讲解php.ini中如何修改上传文件大小限制。需要修改的设置有三个地方,请看下文:1、 修…

java调用 火眼臻睛,火眼臻睛车牌识别SDK评测

【CPS中安网 cps.com.cn】CPS LAB总评:用专业角度解读产品--CPS评测中心对火眼臻睛车牌识别SDK进行了全面评测,火眼臻睛车牌识别SDK在综合识别率、车牌定位成功率、大角度下的识别率、夜间环境下的识别率、极端环境下的识别率、支持的最小车牌像素宽度等测试表现,都位于行业前列…

php 获取 uri,获取URI地址

前台和后台的URI路由地址:APP目录/控制器文件/方法函数会员中心的URI路由地址:member/APP目录/控制器文件/方法函数通过动态地址获取URL:index.php?sAPP目录&c控制器文件&m方法函数获取当控制器的URI地址:\Phpcmf\Service::L(Router…

java各层级限流对比,面试官说:来谈谈限流-从概念到实现,一问你就懵逼了?...

后端服务的接口都是有访问上限的,如果外部qps或并发量超过了访问上限会导致应用瘫痪。所以一般都会对接口调用加上限流保护,防止超出预期的请求导致系统故障。从限流类型来说一般来说分为两种:并发数限流和qps限流,并发数限流就是限制同一时刻…

php 生成验证码干扰元素,PHP生成指定位数验证码与可控干扰元素第二篇

这篇文章介绍的内容是关于PHP生成指定位数验证码与可控干扰元素第二篇&#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下生成验证码&#xff1a;文件名为&#xff1a;buildVerifyCode.func.php<?php //生成指定位数可控混合验…

mysql and 和where,关于mysql:连接sql查询中where和and子句的区别

本问题已经有最佳答案&#xff0c;请猛点这里访问。下面两个SQL查询有什么区别和号根据以下两个测试结果速度更快(237比460)。据我所知&#xff0c;这是一个标准。。氧化镁不&#xff0c;有细微的差别&#xff0c;你不能说没有差别除了语法之外没有别的区别。虽然只有一个简短的…

matlab里输出恒压的逆变器,基于IGBT逆变器的异步电机变频调速系统的MATLAB仿真...

异步电机变频调速系统电路仿真模型如图(4)所示。直流电压不621V&#xff0c;逆变器为IGBT 的三相半桥逆变器&#xff0c;电机为异步电机模块&#xff0c;其主电路由直流电压源、逆变器和电机依次相连。图(4)变频调速系统控制部分&#xff0c;利用“Step”模块设定频率指令f1*&a…

php 获取京东交易账号,PHP爬虫爬取京东列表

这里使用到了一个php插件下面是源码simple_html_dom.phpdefined(IN_ECS);define(HDOM_TYPE_ELEMENT, 1);define(HDOM_TYPE_COMMENT, 2);define(HDOM_TYPE_TEXT, 3);define(HDOM_TYPE_ENDTAG, 4);define(HDOM_TYPE_ROOT, 5);define(HDOM_TYPE_UNKNOWN, 6);define(HDOM_QUOTE_DOU…

星模php格,extend/base/TencentSms.php · 纵之格/ShopXO - Gitee.com

// ----------------------------------------------------------------------// | ShopXO 国内领先企业级B2C免费开源电商系统// ----------------------------------------------------------------------// | Copyright (c) 2011~2019 http://shopxo.net All rights reserve…

matlab中云模型,云模型简介与个人理解matlab程序.doc

.........................专业资料分享随着不确定性研究的深入&#xff0c;越来越多的科学家相信&#xff0c;不确定性是这个世界的魅力所在&#xff0c;只有不确定性本身才是确定的。在众多的不确定性中&#xff0c; HYPERLINK "/view/606111.htm" \t "_blank…

php dns刷新,Windows DNS缓存自动刷新

Windows DNS缓存自动刷新admin • 2018 年 09 月 04 日DNS(域名服务器)DNS(Domain Name Server)是进行域名和与之相对应的ip地址转换的服务器。DNS中保存了一张域名和与之相应的ip地址的表&#xff0c;以解析消息的域名。DNS轮训在统一主机添加多条A记录&#xff0c;这就是DNS轮…

php日期相减函数,倒计时函数_计算两个时间相差值_PHP函数

**PHP倒计时函数、求两个日期时间之间相差的时间函数、计算时差函数_PHP函数笔记**php/*** 求两个日期时间之间相差的时间* (针对1970年1月1日之后&#xff0c;求之前可以采用泰勒公式)* param string $day1 2016-10-01 10:01:08* param string $day2 2020-10-01 10:01:08* ret…

jq上传file到php,jQuery文件上传

插件描述&#xff1a;这是一个文件上传的展示&#xff0c;看很多插件&#xff0c;并不满意就自己写了一个&#xff0c;可能兼容性不是很好&#xff0c;没有准确进行测试过&#xff0c;还请各位不吝赐教更新时间&#xff1a;2019/3/25 下午8:53:17更新说明&#xff1a;修复上传成…

oracle导出建表主键,oracle主键自动生成 配合hibernate的生成策略详解

hibernate配合oracle自动生成主键策略有两种方法&#xff1a;A)设置ID的增长策略是sequence&#xff0c;同时指定sequence的名字&#xff0c;最好每个表建一个sequence&#xff0c;此种做法就如同MS-SQL,MY-SQL中的自动增长一样&#xff0c;不需要创建触发器&#xff0c;具体的…

oracle找到表的位置,查看Oracle表中的指定记录在数据文件中的位置

查看Oracle表中的指定记录位置select rowid,user_id from sshr.xx_user where user_id3010586select rowid,dbms_rowid.rowid_object(rowid) object_id,dbms_rowid.rowid_relative_fno(rowid) file_id,dbms_rowid.rowid_block_number(rowid) block_id,dbms_rowid.rowid_row_num…

oracle异常抛出,ORACLE 存储过程异常捕获并抛出

for tab_name in tables loopexecute immediate drop table ||tab_name; --此处可能会报错end loop;当前情况是&#xff0c;循环表&#xff0c;进行删除&#xff0c;如果出现表不存在&#xff0c;则会异常中断&#xff0c;导致整个存储过程挂掉&#xff0c;需求是要能跳过错误的…

oracle的主目录怎么删除,删除oracle数据库卸载

oracle 10g在win上卸载软件环境1 、Windows XPOracle 10g2、 Oracle安装路径为 d:\Oracle实现方法1 、开始—设置—控制面板—管理工具—服务停止所有 Oracle服务;Or acleUfiCQiisoXeorcl 名称 I细I好I启动类型I aat为ffetwork Frov-isi. 「, 巴SOrae le StrTice…

oracle数据库相关知识,Oracle数据库相关知识点复习

一.Oracle数据库中常用的数据类型varchar2(长度)可变长字符串char(长度)定长number()表示整数或者浮点数number(8) number(8,2)clog字符的大对象blog二进制的大对象二.数据库查询1)SELECT语句从表中提取查询数据.语法为SELECT [DISTINCT] {column1,column2,…} FROM tablename …

Linux中批量创建空白文件,在Linux中批量创建和修改文件或目录

1. 用一条命令直接创建[fredRoyalmile Auto_Ops]$ touch seq 10[fredRoyalmile Auto_Ops]$ ls1 10 2 3 4 5 6 7 8 9[fredRoyalmile Auto_Ops]$ touch linux-{A..G}[fredRoyalmile Auto_Ops]$ lslinux-A linux-B linux-C linux-D linux-E linux-F linux-G[fredRo…