Java NIO 之 Buffer(缓冲区)
一 Buffer(缓冲区)介绍
Java NIO Buffers用于和NIO Channel交互。 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels.
Buffer本质上就是一块内存区,可以用来写入数据,并在稍后读取出来。这块内存被NIO Buffer包裹起来,对外提供一系列的读写方便开发的接口。
在Java NIO中使用的核心缓冲区如下(覆盖了通过I/O发送的基本数据类型:byte, char、short, int, long, float, double ,long):
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- FloatBuffer
- DoubleBuffer
- LongBuffer
利用Buffer读写数据,通常遵循四个步骤:
- 把数据写入buffer;
- 调用flip;
- 从Buffer中读取数据;
- 调用buffer.clear()或者buffer.compact()。
当写入数据到buffer中时,buffer会记录已经写入的数据大小。当需要读数据时,通过 flip() 方法把buffer从写模式调整为读模式;在读模式下,可以读取所有已经写入的数据。
当读取完数据后,需要清空buffer,以满足后续写入操作。清空buffer有两种方式:调用 clear() 或 compact() 方法。clear会清空整个buffer,compact则只清空已读取的数据,未被读取的数据会被移动到buffer的开始位置,写入位置则近跟着未读数据之后。
Buffer的容量,位置,上限(Buffer Capacity, Position and Limit)
Buffer缓冲区实质上就是一块内存,用于写入数据,也供后续再次读取数据。这块内存被NIO Buffer管理,并提供一系列的方法用于更简单的操作这块内存。
一个Buffer有三个属性是必须掌握的,分别是:
- capacity容量
- position位置
- limit限制
position和limit的具体含义取决于当前buffer的模式。capacity在两种模式下都表示容量。
下面有张示例图,描诉了读写模式下position和limit的含义:
容量(Capacity)
作为一块内存,buffer有一个固定的大小,叫做capacit(容量)。也就是最多只能写入容量值得字节,整形等数据。一旦buffer写满了就需要清空已读数据以便下次继续写入新的数据。
位置(Position)
当写入数据到Buffer的时候需要从一个确定的位置开始,默认初始化时这个位置position为0,一旦写入了数据比如一个字节,整形数据,那么position的值就会指向数据之后的一个单元,position最大可以到capacity-1.
当从Buffer读取数据时,也需要从一个确定的位置开始。buffer从写入模式变为读取模式时,position会归零,每次读取后,position向后移动。
上限(Limit)
在写模式,limit的含义是我们所能写入的最大数据量,它等同于buffer的容量。
一旦切换到读模式,limit则代表我们所能读取的最大数据量,他的值等同于写模式下position的位置。换句话说,您可以读取与写入数量相同的字节数(限制设置为写入的字节数,由位置标记)。
二 Buffer的常见方法
方法 | 介绍 |
---|---|
abstract Object array() | 返回支持此缓冲区的数组 (可选操作) |
abstract int arrayOffset() | 返回该缓冲区的缓冲区的第一个元素的背衬数组中的偏移量 (可选操作) |
int capacity() | 返回此缓冲区的容量 |
Buffer clear() | 清除此缓存区。将position = 0;limit = capacity;mark = -1; |
Buffer flip() | flip()方法可以吧Buffer从写模式切换到读模式。调用flip方法会把position归零,并设置limit为之前的position的值。 也就是说,现在position代表的是读取位置,limit标示的是已写入的数据位置。 |
abstract boolean hasArray() | 告诉这个缓冲区是否由可访问的数组支持 |
boolean hasRemaining() | return position < limit,返回是否还有未读内容 |
abstract boolean isDirect() | 判断个缓冲区是否为 direct |
abstract boolean isReadOnly() | 判断告知这个缓冲区是否是只读的 |
int limit() | 返回此缓冲区的限制 |
Buffer position(int newPosition) | 设置这个缓冲区的位置 |
int remaining() | return limit - position; 返回limit和position之间相对位置差 |
Buffer rewind() | 把position设为0,mark设为-1,不改变limit的值 |
Buffer mark() | 将此缓冲区的标记设置在其位置 |
三 Buffer的使用方式/方法介绍
分配缓冲区(Allocating a Buffer)
为了获得缓冲区对象,我们必须首先分配一个缓冲区。在每个Buffer类中,allocate()方法用于分配缓冲区。
下面来看看ByteBuffer分配容量为28字节的例子:
ByteBuffer buf = ByteBuffer.allocate(28);
下面来看看另一个示例:CharBuffer分配空间大小为2048个字符。
CharBuffer buf = CharBuffer.allocate(2048);
写入数据到缓冲区(Writing Data to a Buffer)
写数据到Buffer有两种方法:
- 从Channel中写数据到Buffer
- 手动写数据到Buffer,调用put方法
下面是一个实例,演示从Channel写数据到Buffer:
int bytesRead = inChannel.read(buf); //read into buffer.
通过put写数据:
buf.put(127);
put方法有很多不同版本,对应不同的写数据方法。例如把数据写到特定的位置,或者把一个字节数据写入buffer。看考JavaDoc文档可以查阅的更多数据。
翻转(flip())
flip()方法可以吧Buffer从写模式切换到读模式。调用flip方法会把position归零,并设置limit为之前的position的值。 也就是说,现在position代表的是读取位置,limit标示的是已写入的数据位置。
从Buffer读取数据(Reading Data from a Buffer)
从Buffer读数据也有两种方式。
- 从buffer读数据到channel
- 从buffer直接读取数据,调用get方法
读取数据到channel的例子:
int bytesWritten = inChannel.write(buf);
调用get读取数据的例子:
byte aByte = buf.get();
get也有诸多版本,对应了不同的读取方式。
rewind()
Buffer.rewind()方法将position置为0,这样我们可以重复读取buffer中的数据。limit保持不变。
clear() and compact()
一旦我们从buffer中读取完数据,需要复用buffer为下次写数据做准备。只需要调用clear()或compact()方法。
如果调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,只是这些标记告诉我们可以从哪里开始往Buffer里写数据。
如果Buffer还有一些数据没有读取完,调用clear就会导致这部分数据被“遗忘”,因为我们没有标记这部分数据未读。
针对这种情况,如果需要保留未读数据,那么可以使用compact。 因此 compact() 和 clear() 的区别就在于: 对未读数据的处理,是保留这部分数据还是一起清空 。
mark()与reset()方法
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。例如:
buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset(); //set position back to mark.
equals() and compareTo()
可以用eqauls和compareTo比较两个buffer
equals():
判断两个buffer相对,需满足:
- 类型相同
- buffer中剩余字节数相同
- 所有剩余字节相等
从上面的三个条件可以看出,equals只比较buffer中的部分内容,并不会去比较每一个元素。
compareTo():
compareTo也是比较buffer中的剩余元素,只不过这个方法适用于比较排序的:
四 Buffer常用方法测试
这里以ByteBuffer为例子说明抽象类Buffer的实现类的一些常见方法的使用:
package channel;
import java.nio.ByteBuffer;
public class ByteBufferMethods {
public static void main(String args[]){
//分配缓冲区(Allocating a Buffer)
ByteBuffer buffer = ByteBuffer.allocate(33);
System.out.println("-------------Test reset-------------");
//clear()方法,position将被设回0,limit被设置成 capacity的值
buffer.clear();
// 设置这个缓冲区的位置
buffer.position(5);
//将此缓冲区的标记设置在其位置。没有buffer.mark();这句话会报错
buffer.mark();
buffer.position(10);
System.out.println("before reset: " + buffer);
//将此缓冲区的位置重置为先前标记的位置。(buffer.position(5))
buffer.reset();
System.out.println("after reset: " + buffer);
System.out.println("-------------Test rewind-------------");
buffer.clear();
buffer.position(10);
//返回此缓冲区的限制。
buffer.limit(15);
System.out.println("before rewind: " + buffer);
//把position设为0,mark设为-1,不改变limit的值
buffer.rewind();
System.out.println("before rewind: " + buffer);
System.out.println("-------------Test compact-------------");
buffer.clear();
buffer.put("abcd".getBytes());
System.out.println("before compact: " + buffer);
System.out.println(new String(buffer.array()));
//limit = position;position = 0;mark = -1; 翻转,也就是让flip之后的position到limit这块区域变成之前的0到position这块,
//翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态
buffer.flip();
System.out.println("after flip: " + buffer);
//get()方法:相对读,从position位置读取一个byte,并将position+1,为下次读写作准备
System.out.println((char) buffer.get());
System.out.println((char) buffer.get());
System.out.println((char) buffer.get());
System.out.println("after three gets: " + buffer);
System.out.println("\t" + new String(buffer.array()));
//把从position到limit中的内容移到0到limit-position的区域内,position和limit的取值也分别变成limit-position、capacity。
// 如果先将positon设置到limit,再compact,那么相当于clear()
buffer.compact();
System.out.println("after compact: " + buffer);
System.out.println("\t" + new String(buffer.array()));
System.out.println("-------------Test get-------------");
buffer = ByteBuffer.allocate(32);
buffer.put((byte) 'a').put((byte) 'b').put((byte) 'c').put((byte) 'd')
.put((byte) 'e').put((byte) 'f');
System.out.println("before flip(): " + buffer);
// 转换为读取模式
buffer.flip();
System.out.println("before get(): " + buffer);
System.out.println((char) buffer.get());
System.out.println("after get(): " + buffer);
// get(index)不影响position的值
System.out.println((char) buffer.get(2));
System.out.println("after get(index): " + buffer);
byte[] dst = new byte[10];
buffer.get(dst, 0, 2);
System.out.println("after get(dst, 0, 2): " + buffer);
System.out.println("\t dst:" + new String(dst));
System.out.println("buffer now is: " + buffer);
System.out.println("\t" + new String(buffer.array()));
System.out.println("-------------Test put-------------");
ByteBuffer bb = ByteBuffer.allocate(32);
System.out.println("before put(byte): " + bb);
System.out.println("after put(byte): " + bb.put((byte) 'z'));
System.out.println("\t" + bb.put(2, (byte) 'c'));
// put(2,(byte) 'c')不改变position的位置
System.out.println("after put(2,(byte) 'c'): " + bb);
System.out.println("\t" + new String(bb.array()));
// 这里的buffer是 abcdef[pos=3 lim=6 cap=32]
bb.put(buffer);
System.out.println("after put(buffer): " + bb);
System.out.println("\t" + new String(bb.array()));
}
}
如果大家想搭建个人博客(一般使用的是第三方WordPress搭建或者你也可以使用Tale等开源博客系统搭建,非常方便)或者说使用redis数据库、负载均衡等等第三方服务的话,推荐大家使用阿里云,客观角度来讲,阿里云的服务与质量都是最好的,而且学生优惠特别大,一年一下也就100多块钱。这里是我的优惠券地址(我本人使用的是轻量级服务器):优惠券地址
参考:
欢迎关注我的微信公众号:"Java面试通关手册"(一个有温度的微信公众号,期待与你共同进步~~~坚持原创,分享美文,分享各种Java学习资源):
Java NIO 之 Buffer(缓冲区)的更多相关文章
- Java NIO中的缓冲区Buffer(一)缓冲区基础
什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的 ...
- Java NIO之Buffer(缓冲区)
Java NIO中的缓存区(Buffer)用于和通道(Channel)进行交互.数据是从通道读入缓冲区,从缓冲区写入到通道中的. 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这 ...
- Java NIO 之 Buffer
Java NIO 之 Buffer Java NIO (Non Blocking IO 或者 New IO)是一种非阻塞IO的实现.NIO通过Channel.Buffer.Selector几个组件的协 ...
- JAVA NIO简介-- Buffer、Channel、Charset 、直接缓冲区、分散和聚集、文件锁
IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. Java标准io回顾 在Java1.4之前的I/O系统中,提供 ...
- Java NIO中的缓冲区Buffer(二)创建/复制缓冲区
创建缓冲区的方式 主要有以下两种方式创建缓冲区: 1.调用allocate方法 2.调用wrap方法 我们将以charBuffer为例,阐述各个方法的含义: allocate方法创建缓冲区 调用all ...
- 【Java nio】buffer
package com.slp.nio; import org.junit.Test; import java.nio.ByteBuffer; /** * Created by sanglp on 2 ...
- java nio之Buffer
一.JAVA NIO 是在和channel交互的时候使用的.Channel将数据读入缓冲区,然后我们又从缓冲区访问数据.写数据时,首先将要发送的数据按顺序填入缓冲区.基本上,缓冲区只是一个列表,它的所 ...
- Java NIO:Buffer、Channel 和 Selector
Buffer 一个 Buffer 本质上是内存中的一块,我们可以将数据写入这块内存,之后从这块内存获取数据. java.nio 定义了以下几个 Buffer 的实现,这个图读者应该也在不少地方见过了吧 ...
- Java NIO(2):缓冲区基础
缓冲区(Buffer)对象是面向块的I/O的基础,也是NIO的核心对象之一.在NIO中每一次I/O操作都离不开Buffer,每一次的读和写都是针对Buffer操作的.Buffer在实现上本质是一个数组 ...
随机推荐
- 【MOOC EXP】Linux内核分析实验一报告
程涵 原创博客 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 [反汇编一个简单的C程序] 实验 ...
- Beta冲刺——day7
Beta冲刺--day7 作业链接 Beta冲刺随笔集 github地址 团队成员 031602636 许舒玲(队长) 031602237 吴杰婷 031602220 雷博浩 031602134 王龙 ...
- 功能WBS分解
小组名称:飞天小女警 项目名称:礼物挑选小工具 小组成员:沈柏杉(组长).程媛媛.杨钰宁.谭力铭 代码地址:HTTPS: https://git.coding.net/shenbaishan/GIFT ...
- #Leetcode# 917. Reverse Only Letters
https://leetcode.com/problems/reverse-only-letters/ Given a string S, return the "reversed" ...
- Mac安装Appium的Android环境
1.下载android sdk http://down.tech.sina.com.cn/page/45703.html 2.解压,配置环境变量 ANDROID_HOME=/Users/wp/ ...
- java自定义注解学习(三)_注解解析及应用
上篇文章已经介绍了注解的基本构成信息.这篇文章,主要介绍注解的解析.毕竟你只声明了注解,是没有用的.需要进行解析.主要就是利用反射机制在运行时进行查看和利用这些信息 常用方法汇总 在Class.Fie ...
- vagrant 入门
0.说明 本门所有的操作都是基于mac.windows可做相应调整,大体流程是一样的. 1.什么是vagrant 通俗来讲vagrant是一个通过命令行,来管理虚拟机的工具,vagrant本身并没有提 ...
- 在delphi中我用DBGrid选择多条记录,如何一次把选择的多条记录删掉
procedure TForm1.btnDoSumClick(Sender: TObject);var i: Integer;begin if DBGrid1.SelectedRows.Count ...
- canvas技术整理
canvas技术整理 html <canvas id= "canvas"></canvas> javascript var canvas = documen ...
- iOS—— iOS 内存管理:增长+泄漏
1.如果是循环中局部变量data没有释放导致的,给NSData 手动添加释放池 @autoreleasepool { your code } 2.url 转变字典key值的时候出现内存暴增! //设置 ...