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(); }