java NIO Buffer 详解(1)
1.java.io 最为核心的概念是流(stream),面向流的编程,要么输入流要么输出流,二者不可兼具;
2.java.nio 中拥有3个核心概念:
Selector Channel, Buffer ,在java nio 中我们面向的是块(block)或是缓冲区(buffer) 编程 ;Buffer 本身就是一块内存,底层实现上,是个数组,
数据的读写都是通过Buffer 来实现的,flip 进行状态的翻转,读写切换;
java 的种原生数据类型中都有各自对应的Buffer 如:IntBuffer LongBuffer ByteBuffer CharBuffer 等,没有boolean
Channel 是指可以向其写入数据或者是从中读取数据的对象,类似 io stream,所有数据读写是通过BUFFER 执行的,永远不会出现直接向Channel 写入数据的情况以及直接向channel 读取数据的情况;channel 是双向的,可以进行读写的操作;可以更好反映底层操作情况
3. 关于NIO Buffer 中3个重要状态属性含义:position: limit: capacity:
Buffer 源码解读: A container for data of a specific primitive type. Buffer 是一个具体的原始类型(除去boolean)的数据容器 A buffer is a linear, finite sequence of elements of a specific primitive type. Aside from its content, the essential properties of a buffer are its capacity, limit, and position: buffer 是一个 线性的,限定的,序列元素的 具体原始类型,除了他的内容之外, 一个buffer 必不可少的特性就是 capacity(容量)。limit .position这三部分 A buffer's <i>capacity</i> is the number of elements it contains.
The capacity of a buffer is never negative and never changes. capacity 是 buffer 容器里元素的个数, 它从来不会是负数,也不会被改变,在我们初始化的时候就会被我们自己定义它的大小
A buffer's <i>position</i> is the index of the next element to be
* read or written. A buffer's position is never negative and is never
* greater than its limit.
position(位置) 是 读写下一元素的索引,position 也是从来不会是负数,从来不会大于limit
<p> A buffer's <i>limit</i> is the index of the first element that should
* not be read or written. A buffer's limit is never negative and is never
* greater than its capacity. </p>
limit(限定) 是不应该被读到或者被写到的元素的首位置, 它不会是负数,不会超过 capacity
经过以上源码解读,可以看出 capacity 在读写过程中是个不变的值,在定义的时候就固定了。
position 是随着channel 读写变换的
limit 是一个限定,用来告诉 position 应该只能写的这里,只能读到这里
比较就是 position < =limit <= capacity
即:
参数 |
写模式 |
读模式 |
position |
当前写入的单位数据数量。 |
当前读取的单位数据位置。 |
limit |
代表最多能写多少单位数据和容量是一样的。 |
代表最多能读多少单位数据,和之前写入的单位数据量一致。 |
capacity |
buffer 容量 |
buffer 容量 |
<p> There is one subclass of this class for each non-boolean primitive type.
每一个非布尔的原始类型在这个class 里都会有一个子类
<p> Transferring data
Each subclass of this class defines two categories of <i>get</i> and
* <i>put</i> operations: </p>
数据的传输 ,每一个子类都会定义 get 和put 两种方式操作
*<p> <i>Relative</i> operations read or write one or more elements starting
* at the current position and then increment the position by the number of
* elements transferred. If the requested transfer exceeds the limit then a
* relative <i>get</i> operation throws a {@link BufferUnderflowException}
* and a relative <i>put</i> operation throws a {@link
* BufferOverflowException}; in either case, no data is transferred. </p>
相对的 操作读和写一个或者多个元素在目前的position(位置坐标)通过传递元素的数量增加这个position 位置
如果请求超出了limit 的范围,相对的get 操作会抛出BufferUnderflowException 异常
put 方法操作会抛出BufferOverflowException 异常
无论发生何种情况,都没有数据传递
* <p> <i>Absolute</i> operations take an explicit element index and do not
* affect the position. Absolute <i>get</i> and <i>put</i> operations throw
* an {@link IndexOutOfBoundsException} if the index argument exceeds the
* limit. </p>
绝对的操作 进行明确一个元素的索引,不会影响postion 位置,如果索引参数超出了limit 就会抛出 IndexOutOfBoundsException 异常
标记与重置的概念
* <h2> Marking and resetting </h2>
*
* <p> A buffer's <i>mark</i> is the index to which its position will be reset
* when the {@link #reset reset} method is invoked. The mark is not always
* defined, but when it is defined it is never negative and is never greater
* than the position. If the mark is defined then it is discarded when the
* position or the limit is adjusted to a value smaller than the mark. If the
* mark is not defined then invoking the {@link #reset reset} method causes an
* {@link InvalidMarkException} to be thrown. 标记与重置 buffer 的 标记是position 将会被重置的索引,当reset 方法被调用的时候。这个标记不总是被
定义的,但是当他定义的时候,他的值不应该是负数以及它不会超出position 的值,
如果标记被定义,当position 或者limit 调整成了一个比标记还小的值,那么这个标记就会被丢弃(-1)
如果标记没有被定义,但是你调用了reset 方法,将会抛出 InvalidMarkException 异常 一句话,mark 是一个标记,当调用reset 方法时候,position 位置将会是mark 的标记的索引位置
构建一个新的buffer,构建buffer 时候做了什么事情呢?
* <p> A newly-created buffer always has a position of zero and a mark that is
* undefined. The initial limit may be zero, or it may be some other value
* that depends upon the type of the buffer and the manner in which it is
* constructed. Each element of a newly-allocated buffer is initialized
* to zero. 一个新创建的buffer 总会有一个从0开始的position 以及 未被定义的mark(-1)
初始的limit可能是0 或者是其他的值主要取决于你buffer 的类型和构造函数的方式
新分配的缓冲区每一个元素初始化为零
-------------例子详解------------
我们以IntBuffer 为例,进行构造它的Buffer 对象
IntBuffer buffer =IntBuffer.allocate(10);//分配一个容量为10的IntBuffer
我们跟踪源码点进去,第一步,可以看出它去创建了HeapIntBuffer 对象,
public static IntBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapIntBuffer(capacity, capacity);
}
点进去 HeapIntBuffer,可以差不多看到刚开始构造时候,capacity = limit 值
HeapIntBuffer(int cap, int lim) { // package-private
super(-1, 0, lim, cap, new int[cap], 0);
}
点进去super,看到会创建出一个容量为capacity的数组,所以说buffer 底层是数组,且初始化的position 为0 ,mark 为-1(即无效)
IntBuffer(int mark, int pos, int lim, int cap, // package-private
int[] hb, int offset)
{
super(mark, pos, lim, cap);
this.hb = hb;
this.offset = offset;
}
常用的方法文档解释
clear 方法 * <li><p> {@link #clear} makes a buffer ready for a new sequence of
* channel-read or relative <i>put</i> operations: It sets the limit to the
* capacity and the position to zero. </p></li> clear 方法 使buffer 准备以一个全新的方式进行 channel -read 操作或者相对的buffer put 操作
它会将limit 值设置成buffer capacity的值,以及position 从0 开始; 如下源码方法:可以看出clear 方法执行的操作。position 置位0 limit 等于 capacity ,清除标记-1 public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
} ------例子详解------
public static void main(String[] args) {
//分配一个容量为10 IntBuffer 底层是数组
IntBuffer buffer =IntBuffer.allocate(10);
//在够着函数创建之后,就会有了capacity limit position 的 值了
System.err.println("起始capacity值==>"+buffer.capacity());
System.err.println("起始limit值==>"+buffer.limit());
System.err.println("起始position值==>"+buffer.position());
for(int i =0;i<buffer.capacity();i++) {
//遍历在每一个buffer 节点 插入随机数
buffer.put(new SecureRandom().nextInt(10));
//打印当前的 capacity limit position 的 值;
System.out.println("循环capacity值==>"+buffer.capacity());
System.out.println("循环limit值==>"+buffer.limit());
System.out.println("循环position值==>"+buffer.position());
}
//buffer clear 方法调用
buffer.clear();
System.err.println("clear 后capacity值==>"+buffer.capacity());
System.err.println("clear 后limit值==>"+buffer.limit());
System.err.println("clear 后position值==>"+buffer.position());
//......再进行自己的put 操作
}
flip 方法 * <li><p> {@link #flip} makes a buffer ready for a new sequence of
* channel-write or relative <i>get</i> operations: It sets the limit to the
* current position and then sets the position to zero. </p></li> clear 方法 使buffer 准备以一个全新的方式进行 channel -write操作或者相对的buffer get操作
他会将limit 值设置为当前position 值,将position 设置为0 如下源码方法:可以看出flip 方法执行的操作。 public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
} -------例子详解
public static void main(String[] args) {
//分配一个容量为10 IntBuffer 底层是数组
IntBuffer buffer =IntBuffer.allocate(10);
//在够着函数创建之后,就会有了capacity limit position 的 值了
System.err.println("起始capacity值==>"+buffer.capacity());
System.err.println("起始limit值==>"+buffer.limit());
System.err.println("起始position值==>"+buffer.position());
//为了看到limit 效果,这里设置循环5次
for(int i =0;i<5;i++) {
//遍历在每一个buffer 节点 插入随机数
buffer.put(new SecureRandom().nextInt(10));
//打印当前的 capacity limit position 的 值;
System.out.println("循环capacity值==>"+buffer.capacity());
System.out.println("循环limit值==>"+buffer.limit());
System.out.println("循环position值==>"+buffer.position());
}
//buffer flip 方法调用
buffer.flip();
System.err.println("flip 后capacity值==>"+buffer.capacity());
System.err.println("flip 后limit值==>"+buffer.limit());
System.err.println("flip 后position值==>"+buffer.position());
while(buffer.hasRemaining()) {//=position < limit的时候循环;
System.out.println("buffer的值===>"+buffer.get());
}
}
rewind(倒回) 方法; * <li><p> {@link #rewind} makes a buffer ready for re-reading the data that
* it already contains: It leaves the limit unchanged and sets the position
* to zero. </p></li>
*
* </ul> rewind 方法是在已经存在的容器里重新读取数据做好准备,他会保持limit 不改变并且使position 为0 如下源码方法:可以看出rewind方法执行的操作。 public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
public static void main(String[] args) throws Exception { FileInputStream file = new FileInputStream("c:\\demo.txt");
//获取channel 对象
FileChannel fileChannel=file.getChannel();
//读写操作都会用到buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(5112);
//channel 读取数据到buffer
fileChannel.read(byteBuffer); //状态翻转,切换为读
byteBuffer.flip(); while(byteBuffer.hasRemaining()) { byte b =byteBuffer.get();
System.out.println((char)b);
} file.close();
}
java NIO Buffer 详解(1)的更多相关文章
- Java NIO Buffer详解
一.ByteBuffer类型化的put与get方法 /** * ByteBuffer类型化的put与get方法 */ public class NioTest5 { public static voi ...
- Java NIO API详解
在JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的同步(blocking)API.对于大多数应用来说,这样的API使用很方便,然而,一些对性能要求较高的应用,尤其是服务端 ...
- Java NIO API详解(转)
原文连接: http://www.blogjava.net/19851985lili/articles/93524.html 感谢原作者 NIO API 主要集中在 java.nio 和它的 subp ...
- Java NIO全面详解(看这篇就够了)
很多技术框架都使用NIO技术,学习和掌握Java NIO技术对于高性能.高并发网络的应用是非常关键的@mikechen NIO简介 NIO 中的 N 可以理解为 Non-blocking,不单纯是 N ...
- Java NIO 的前生今世 之四 NIO Selector 详解
Selector Selector 允许一个单一的线程来操作多个 Channel. 如果我们的应用程序中使用了多个 Channel, 那么使用 Selector 很方便的实现这样的目的, 但是因为在一 ...
- Java String类详解
Java String类详解 Java字符串类(java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉又陌生. 类结构: public final ...
- JAVA IO 类库详解
JAVA IO类库详解 一.InputStream类 1.表示字节输入流的所有类的超类,是一个抽象类. 2.类的方法 方法 参数 功能详述 InputStream 构造方法 available 如果用 ...
- 前端后台以及游戏中使用Google Protocol Buffer详解
前端后台以及游戏中使用Google Protocol Buffer详解 0.什么是protoBuf protoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,与XML相比,protoBuf更 ...
- Thrift实现C#调用Java开发步骤详解
概述 Thrift实现C#调用Java开发步骤详解 详细 代码下载:http://www.demodashi.com/demo/10946.html Apache Thrift 是 Facebook ...
随机推荐
- Oracle 学习总结 - 表和索引的性能优化
表的性能 表的性能取决于创建表之前所应用的数据库特性,数据库->表空间->表,创建数据库时确保为每个用户创建一个默认的永久表空间和临时表空间并使用本地管理,创建表空间设为本地管理并且自动段 ...
- Linux安装卸载jdk1.8
首先到官网下载 Linux x64 182.87 MB jdk-8u191-linux-x64.tar.gz https://www.oracle.com/technetwork/java/java ...
- NetStream 记录
bufferLength : Number [只读] 数据当前存在于缓冲区中的秒数.(已进入缓冲区的秒数) bufferTime : Number 指定在开始显示流之前需要多长时间将消息存入缓冲区.( ...
- bug提单规范
一.提单模板 标题:[项目组][模块][子模块][发生原因]问题简要描述描述:[预置条件] 有就写清楚,没有就写无[操作步骤]1.XXXXX2.XXXXXX3.XXXXX[实际结果] XXXXX[预期 ...
- 关于AVAYA CM6和Asterisk(Freepbx)对接的笔记
最重要的两份参考文档: http://blog.sina.com.cn/s/blog_6db134880102vipo.html https://personalprojects.wordpress. ...
- MySQL字符集介绍及配置
目录 1.MySQL编码集 2.修改字符集 3.MySQL数据库中字符集转换流程 4.修改现有字符集 1.MySQL编码集 查看MySQL支持的字符集 mysql> show character ...
- c 时间 学习
linux #include <stdio.h> #include <time.h> int main(int argc,char **argv) { //两种时间的获取方法 ...
- 使用DOM的方法获取所有li元素,然后使用jQuery()构造函数把它封装为jQuery对象
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- win命令行下载
1.certutil downloader (1) 保存在当前路径,文件名称同URL eg: certutil.exe -urlcache -split -f https://raw.githubus ...
- ubuntu 下安装和启动SSH 服务
安装OPENSSH 服务端 sudo apt-get install openssh-server 查看进程是否启动 ps -e | grep ssh 删除密钥文件 rm /etc/ssh/ssh_h ...