- 什么是Java NIO
- 缓冲区 Buffer
Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO *** 作。NIO将以更加高效的方式进行文件的读写 *** 作。
Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后 *** 作缓冲区,对数据进行处理。
Channel 负责传输, Buffer 负责存储
Java NIO与IO的区别:
IO:
面向字节流的单向传输。
NIO:
双方通过缓冲区进行数据的读和写。
缓冲区(Buffer):一个用于特定基本数据类型的容器(可以看做数组),由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer
1、缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区。
2、缓冲区存取数据的两个核心方法:
put() : 存入数据到缓冲区中
get() : 获取缓冲区中的数据
3、缓冲区中的四个核心属性:
capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明无论读或写数据均不能改变。
limit : 界限,第一个不应该读取或写入的数据的索引。
(写模式等于capacity容量;读模式表示已经存放了的数据大小:limit位置及limit之后的数据不能进行读写)
position : 位置,表示下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制。
标记 (mark)与重置 (reset): 标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这个 position.
0 <= mark <= position <= limit <= capacity
4、缓冲区的数据 *** 作
Buffer 所有子类提供了两个用于数据 *** 作的方法:get() 与 put() 方法。
获取 Buffer 中的数据
get() :读取单个字节
get(byte[] dst):批量读取多个字节到 dst 中
get(int index):读取指定索引位置的字节(不会移动 position)
放入数据到 Buffer 中
put(byte b):将给定单个字节写入缓冲区的当前位置
put(byte[] src):将 src 中的字节写入缓冲区的当前位置
put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动 position)
@Test public void test2(){ String str = "abcde"; ByteBuffer buf = ByteBuffer.allocate(1024); buf.put(str.getBytes()); buf.flip(); byte[] dst = new byte[buf.limit()]; buf.get(dst, 0, 2); System.out.println(new String(dst, 0, 2));//ab System.out.println(buf.position());//2 //mark() : 标记 buf.mark(); buf.get(dst, 2, 2); System.out.println(new String(dst, 2, 2));//cd System.out.println(buf.position());//4 //reset() : 恢复到 mark 的位置 buf.reset(); System.out.println(buf.position());//2 //判断缓冲区中是否还有剩余数据 if(buf.hasRemaining()){ //获取缓冲区中可以 *** 作的数量 System.out.println(buf.remaining());//3 } } @Test public void test1(){ String str = "abcde"; //1. 分配一个指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println("-----------------allocate()----------------"); System.out.println(buf.position());//0 System.out.println(buf.limit());//1024 System.out.println(buf.capacity());//1024 //2. 利用 put() 存入数据到缓冲区中 buf.put(str.getBytes()); System.out.println("-----------------put()----------------"); System.out.println(buf.position());//5 前面0~4已经用于存放了abcde,所以此时可以进行写 *** 作的位置是5 System.out.println(buf.limit());//1024 System.out.println(buf.capacity());//1024 //3. 读取数据 buf.flip();//将缓冲区的limit界限设置为当前位置,并将当前位置position重置为 0 用于获取数据前的预备 *** 作 System.out.println("-----------------flip()----------------"); System.out.println(buf.position());//0 切换为读数据模式,从头开始 System.out.println(buf.limit());//5 位置5及5之后还没存放数据,所以不能读 System.out.println(buf.capacity());//1024 //4. 利用 get() 读取缓冲区中的数据 byte[] dst = new byte[buf.limit()]; buf.get(dst); System.out.println(new String(dst, 0, dst.length));//abcde System.out.println("-----------------get()----------------"); System.out.println(buf.position());//5 0~4位置已经进行了读 *** 作,所以此时可进行写 *** 作的位置是5 System.out.println(buf.limit());//5 System.out.println(buf.capacity());//1024 //5. rewind() : 可重复读 buf.rewind();//将位置position设为 0, 取消设置的 mark System.out.println("-----------------rewind()----------------"); System.out.println(buf.position());//0 每次执行完rewind 都可以从最开始的位置(0)读取 System.out.println(buf.limit());//5 System.out.println(buf.capacity());//1024 //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态 buf.clear(); System.out.println("-----------------clear()----------------"); System.out.println(buf.position());//0 System.out.println(buf.limit());//1024 System.out.println(buf.capacity());//1024 System.out.println((char)buf.get());//a }
5、直接缓冲区与非直接缓冲区:
非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中。
直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
直接缓冲区:
字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O *** 作。也就是说,在每次调用基础 *** 作系统的一个本机 I/O *** 作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
直接字节缓冲区可以通过调用此类的 allocateDirect() 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O *** 作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。
@Test public void test3(){ //分配直接缓冲区 ByteBuffer buf = ByteBuffer.allocateDirect(1024); System.out.println(buf.isDirect());//true 判断是否是直接缓冲区 }
非直接缓冲区:
传统IO和非直接缓冲区均为这种模式。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)