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 学习总结 - 内存优化
实例内存优化 开启自动内存管理 1. 设置memory_max_target alter system set memory_max_target=1G scope=spfile; /*init.or ...
- C#反射机制详解
反射的定义:审查元数据并收集关於它的类型信息的能力,元数据(编辑后的基本数据单元)就是一大堆表,编译器会创建一个类定义表,一个字段定义表,一个方法定义表等,System.Reflection命名空间包 ...
- JSP基本_EL式
1.EL式下記二種類がある.① ${式} : JSPの出力(レンダリング)時に評価 (JSP2.0から)② #{式} : タグハンドラにより任意のタイミングで評価 (JSP2.1から) 2.オブジェク ...
- JAVA字符串格式化-String.format()的使用【转】
原文地址:https://blog.csdn.net/lonely_fireworks/article/details/7962171 常规类型的格式化 String类的format()方法用于创建格 ...
- 学习JS的心路历程-函式(一)
前几天有间单提到该如何声明函式及在Hositing中会发生什么事,但是函式的奥妙不仅于此. 身为一个使用JS的工程师,我们一定要熟悉函式到比恋人还熟! 这几天将会把函式逐一扒开跟各位一起探讨其中的奥妙 ...
- akka之种子节点
AKKA提供的cluser功能能够很便捷的创建一个分布式应用,在使用cluster时需要配置seed nodes节点,这里对seed nodes节点做一下介绍. AKKA seed nodes 和普通 ...
- clone()与image和 cloneTo()
Mat image = imread("1.png" ) ; Mat image1 ; Mat image1(image) ;//仅是创建了Mat的头部分,image1与image ...
- maven(二)基于intellij idea搭建maven项目
搭建步骤: 1.File -> New Module,进入创建项目窗口. 接着下一步,这里需要注在Properties中添加一个参数 archetypeCatalog=internal,不加这个 ...
- Javascript Iterator
[Javascript Iterator] 1.@@iterator Whenever an object needs to be iterated (such as at the beginning ...
- appium+python自动化测试
appium+python自动化测试 标签(空格分隔): appium 获取APP的包名 1.aapt即Android Asset Packaging Tool,在SDK的build-tools目录下 ...