------Android培训、Java培训、期待与您交流------
一.NIO基本介绍
1.1 缓冲区
缓冲区是一个固定数据量的指定基本类型的数据容器。除内容之外,缓冲区还具有位置和界限,其中位置是要读写的下一个元素的索引,界限是第一个应该读写的元素的索引。基本Buffer类定义了这些属性以及清除、反转和重绕方法,用以标记当前位置,以及将当前位置重置 为前一个标记处。每个非布尔基本类型都有一个缓冲区类。每个类定义了一系列用于将数据移出或移入缓冲区的get和put方法,用于压缩、复制和切片缓冲区的方法,以及用于分配新缓冲区和将现有数组包装到缓冲区中的静态方法。
1.2 字节缓存区的特性
可以将字节缓冲区分配为一个直接缓冲区,在这种情况下,Java 虚拟机将最大限度地直接在缓冲区上执行本机I/O操作。
可以通过mapping将文件区域直接包装到内存中来创建字节缓冲区,在这种情况下,可以使用MappedByteBuffer中定义的几个其他文件相关的操作。
字节缓冲区提供了对其内容的访问(其内容作为任何非布尔基本类型的异类或同类二进制数据序列),访问要么是以big-endian字节顺序进行,要么是以little-endian字节顺序进行。
二.Buffer
2.1 简介
缓冲区是特定基本类型元素的线性有限序列。
2.2 基本属性
缓冲区的容量:它所包含的元素的数量。缓冲区的容量不能为负并且不能更改。
缓冲区的限制:第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量。
缓冲区的位置:下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其限制。
2.3 传输数据
此类的每个子类都定义了两种获取和放置操作:
相对操作读取或写入一个或多个元素,它从当前位置开始,然后将位置增加所传输的元素数。如果请求的传输超出限制,则相对获取操作将抛出 BufferUnderflowException,相对放置操作将抛出 BufferOverflowException;这两种情况下,都没有数据被传输。
绝对操作采用显式元素索引,该操作不影响位置。如果索引参数超出限制,绝对获取操作和放置操作将抛出IndexOutOfBoundsException。
当然,通过适当通道的 I/O 操作(通常与当前位置有关)也可以将数据传输到缓冲区或从缓冲区传出数据。
2.4 做标记和重置
缓冲区的标记是一个索引,在调用reset方法时会将缓冲区的位置重置为该索引。并非总是需要定义标记,但在定义标记时,不能将其定义为负数,并且不能让它大于位置。如果定义了标记,则在将位置或限制调整为小于该标记的值时,该标记将被丢弃。如果未定义标记,那么调用reset方法将导致抛出InvalidMarkException。
2.5 不变式
标记、位置、限制和容量值遵守以下不变式: 0 <= 标记 <= 位置 <= 限制 <= 容量
新创建的缓冲区总有一个0位置和一个未定义的标记。初始限制可以为0,也可以为其他值,这取决于缓冲区类型及其构建方式。一般情况下,缓冲区的初始内容是未定义的。
2.6 清除、反转和重绕
除了访问位置、限制、容量值的方法以及做标记和重置的方法外,此类还定义了以下可对缓冲区进行的操作:clear()使缓冲区为一系列新的通道读取或相对放置操作做好准备:它将限制设置为容量大小,将位置设置为0。flip()使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为0。rewind()使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为0。
2.7 只读缓冲区
每个缓冲区都是可读取的,但并非每个缓冲区都是可写入的。每个缓冲区类的转变方法都被指定为可选操作,当对只读缓冲区调用时,将抛出 ReadOnlyBufferException。只读缓冲区不允许更改其内容,但其标记、位置和限制值是可变的。可以调用其 isReadOnly 方法确定缓冲区是否为只读。
2.8 线程安全
多个当前线程使用缓冲区是不安全的。如果一个缓冲区由不止一个线程使用,则应该通过适当的同步来控制对该缓冲区的访问。
2.9 调用链
指定此类中的方法返回调用它们的缓冲区(否则它们不会返回任何值)。此操作允许将方法调用组成一个链;例如,语句序列
b.flip().position(23).limit(42);
三.ByteBuffer
public class NIOTest {
public static void main(String[] args) {
ByteBuffer Indirect=ByteBuffer.allocate(5);//间接缓冲区
byte b=12;
Indirect.put(b);//put(byte b)向缓冲区加入值时,先把值加入到缓冲区中,然后让当前位置增加1,该方法的调用与位置的当前索引关系很大
Indirect.put(b);
Indirect.put(b);
Indirect.put(b);//此时的索引为4
Indirect.put(b);//只能加入缓冲区分配的个数。此时的索引为5
//put(int index,byte b) 它只负责将值加入到容器,不改变当前位置.
//get() 他除了获取当前的值外,还会让当前的值增加1,该方法的调用与位置的当前索引关系很大
//get(int index) 它只获取当前索引处的值,不会改变当前位置
/* System.out.println(Indirect.get());//该方法在此处调用,会出现异常错误。此时的索引大于缓冲区的容量.只有该方法在调用时的当前索引<Capacity
才不会抛出异常.*/
Indirect.position(0);//改变当前位置为0
System.out.println(Indirect.get());//此时的调用才会得到你所想的值,如果你想得到索引x处的值,此时你应该改变当前的索引为x。
Indirect.position(3);
Indirect.limit(4);//限制当前的索引只能到3。
Indirect.clear();//清除一切设置,恢复到初始,但内容依然存在。
Indirect.position(3);
Indirect.flip(); //反转从0到当前索引
System.out.println(Indirect.get());
Indirect.clear();
System.out.println(Indirect.remaining());
}
}
四.ByteOrder
public class NIOTest {
public static void main(String[] args) {
System.out.println(ByteOrder.nativeOrder()); //看字节码是大端还是小端
}
}
五.CharBuffer
public class NIOTest {
public static void main(String[] args) {
CharBuffer Direct=CharBuffer.allocate(5);//间接缓冲区
char b='A';
Direct.put(b);//put(byte b)向缓冲区加入值时,先把值加入到缓冲区中,然后让当前位置增加1,该方法的调用与位置的当前索引关系很大
Direct.append('B');
Direct.put(b);
Direct.put(b);//此时的索引为4
Direct.put(b);//只能加入缓冲区分配的个数。此时的索引为5
//put(int index,byte b) 它只负责将值加入到容器,不改变当前位置.
//get() 他除了获取当前的值外,还会让当前的值增加1,该方法的调用与位置的当前索引关系很大
//get(int index) 它只获取当前索引处的值,不会改变当前位置
/* System.out.println(Direct.get());//该方法在此处调用,会出现异常错误。此时的索引大于缓冲区的容量.只有该方法在调用时的当前索引<Capacity
才不会抛出异常.*/
Direct.position(0);//改变当前位置为0
System.out.println(Direct.get());//此时的调用才会得到你所想的值,如果你想得到索引x处的值,此时你应该改变当前的索引为x。
Direct.position(3);
Direct.limit(4);//限制当前的索引只能到3。
Direct.clear();//清除一切设置,恢复到初始,但内容依然存在。
Direct.position(3);
Direct.flip(); //反转从0到当前索引
System.out.println(Direct.get());
Direct.clear();
System.out.println(Direct.remaining());
IntStream stream=Direct.chars();
stream.forEach(x->System.out.print((char)x));
}
}
六.IntBuffer
public class NIOTest {
public static void main(String[] args) {
IntBuffer buffer0=IntBuffer.allocate(5);
buffer0.put(1).put(2).put(3).put(4).put(5);
IntBuffer buffer1=buffer0.duplicate();//共享底层数组,但不共享标记.
buffer1.limit(3);
System.out.println(buffer0.get(4));//正确运行
// System.out.println(buffer1.get(4));抛出异常
IntBuffer buffer2=buffer0.asReadOnlyBuffer();//设置为只读
//buffer2.put(0,0);//抛出异常
}
}
七.MappedByteBuffer
7.1.简介:
直接字节缓冲区,其内容是文件的内存映射区域。映射的字节缓冲区是通过FileChannel.map方法创建的。此类用特定于内存映射文件区域的操作扩展ByteBuffer类。映射的字节缓冲区和它所表示的文件映射关系在该缓冲区本身成为垃圾回收缓冲区之前一直保持有效。映射的字节缓冲区的内容可以随时更改,例 如,在此程序或另一个程序更改了对应的映射文件区域的内容的情况下。这些更改是否发生(以及何时发生)与操作系统无关,因此是未指定的。全部或部分映射的字节缓冲区可能随时成为不可访问的,例如,如果我们截取映射的文件。试图访问映射的字节缓冲区的不可访问区域将不会更改缓冲区的内容,并导致在访问时或访问后的某个时刻抛出未指定的异常。因此强烈推荐采取适当的预防措施,以避免此程序或另一个同时运行的程序对映射的文件执行操作(读写文件内容除外除此之外,映射的字节缓冲区的功能与普通的直接字节缓冲区完全相同
public class NIOTest {
public static void main(String[] args) throws Exception {
File file=new File("res.txt");
FileInputStream fin=new FileInputStream("res.txt");
FileChannel channel= fin.getChannel();
MappedByteBuffer map= channel.map(MapMode.READ_ONLY,0,channel.size());
System.out.println(map.remaining());
}
}