一 前言
在JAVA API提供的原I/O中,提供了文件读写,操作,传输的方法。但是存在一个核心的问题,就是这些文件的读写,操作等都是阻塞式,如果当前操作未完成,程序无法向下执行,所以在某种情况下会严重影响I/O,效率。因此,在后期的JDK版本发布中,出现了NIO,NIO包括对缓冲区操作,通道,文件锁,字符集,selector等。下面为大家介绍下缓冲区。
二 缓冲区
缓冲区针对除boolean之外的所有基本数据类型都设置(ByteBuffer(仅其可直接缓冲区),CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer, LongBuffer,ShorBuffer)
缓冲区中存在三个重要变量:position(在buffer执行flip()后重置为0),limit(在buffer执行flip后,为原position值),capactiy(缓冲区大小)
position:写模式下写入数据的下标位(起始0,最大可为capacity),读写模式下为读入数据的下标位(起始0,最大为capacity),最大值可为-1.
limit: 读模式下可以读取的最大数据,此时limit= position,写入模式能够写入多少数据,此时limit=capacity。
capacity:缓冲区的容量。
方法:主要有下列常用方法
alllocate(int length):设置缓冲区长度。
put(Object o):放入缓冲区元素
force(true):将缓冲区数据强制存储到磁盘中。
get(Object o):获取缓冲区元素
slice():创建子缓冲区
wrap(byte[] array, int offset, int length):将数组放到缓冲区
read(charbuffer target)将元素读入缓冲区
flip()重新设置缓冲区,用于从一个缓冲区把数据传输到另一个缓冲区;实现缓冲区读写模式的切换。
clear():清除缓冲区,postion=0,limit=capacity,把缓冲区已读取的数据清除。
compact():清除缓冲区,将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。。
三 应用
1.对缓冲进行放入数据,获取元素:
package com.myd.cn.Nio;
import java.nio.IntBuffer;
/**
* 缓冲区操作:对缓冲进行放入数据,获取元素
* @author MAYADONG
*
*/
public class IntBuffer01 {
public static void main(String[] args) {
//设置缓冲区大小
IntBuffer buf = IntBuffer.allocate(10);
System.out.println("放入数据之前:position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
//放入数据
int [] temp = {1,3,4,5};
buf.put(temp);
System.out.println("放入数据之后:position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
//重设缓冲区(将limit = (当前)positon,position = 0)
buf.flip();
System.out.println("flush之后:position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
System.out.println("输出缓冲区数据");
//输出数据
while(buf.hasRemaining()){
System.out.print(buf.get());
}
}
}
执行程序发现put()之后, postion = 4,limit = capacity = 10,flip()操作之后,发现 position = 0,limit = (原)postion ,capacity = 10;
2.同时读入文件内容,把内容写入其他文件
package com.myd.cn.Nio;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileChannel02 {
public static void main(String[] args) throws IOException {
//定义声明读写的文件路径 文件读写流,以及读写通道
File read = new File("E:"+File.separator+"pay"+File.separator+"read.txt");
File write = new File("E:"+File.separator+"pay"+File.separator+"write.txt");
//定义输入,输出字节流,输入,输出通道
FileInputStream input = null;
FileOutputStream output = null;
//输入输出通道
FileChannel fin = null;
FileChannel fout = null;
//实例化各个变量
input = new FileInputStream(read);
output = new FileOutputStream(write, true);
fin = input.getChannel();
fout = output.getChannel();
//因通道是在缓存区操作的,需要创建缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
int temp = 0;
//将其迭代读取缓冲区
while ((temp = fin.read(buf))!=-1) {
//重置缓存区,重新读取
buf.flip();
System.out.println("position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
//将迭代读取的内容通过写入通道,放入缓存区
fout.write(buf);
System.out.println("position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
//清空缓冲区,再次进行读取,防止内存溢出
buf.clear();
}
try {
fout.close();
fin.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
操作之后,发现读入文件内容时 limit = capacity;缓冲区重置后,limit < capacity;缓冲区清空后,position = limit
在把文件内容写入缓冲去时,要记得flip()缓冲区,进行文件内容的再次读取;
把文件写入缓冲区后,要记得clear()缓冲区,防止因缓冲区设置过小,而造成内存溢出(缓冲区也相当于内存)
3.设置子缓冲区
package com.myd.cn.Nio;
import java.nio.IntBuffer;
public class IntBuffer01 {
public static void main(String[] args) {
//设置缓冲区大小
IntBuffer buf = IntBuffer.allocate(10);
IntBuffer sub = null;
//放入元素
System.out.println("放入数据之前:position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
int [] temp = {1,3,4,5,33,23,4,64,44,6};
buf.put(temp);
System.out.println("放入数据之后:position: "+buf.position()+",limit: "+buf.limit()+",capacity: "+buf.capacity());
//设置子缓冲区
//设置子缓冲区position的值
buf.position(2);
//设置子缓冲区limit值
buf.limit(5);
//设置的position,limit区间为:左闭右开
sub = buf.slice();
//重设缓冲区
buf.flip();
//输出缓冲区
System.out.println("子缓冲区的元素: ");
while (sub.hasRemaining()) {
System.out.print(sub.get()+"\t");
}
}
}
观察发现,子缓冲区根据新设置的position和limit来界定输出,范围区间为左闭又开。
4.直接缓冲区:提高本机对I/O的操作性能
package com.myd.cn.Nio;
import java.nio.ByteBuffer;
/**
* 直接缓冲区:在本机上尽可能的提高本机操作缓冲区的性能
* @author MAYADONG
*
*/
public class DirectBuffer {
public static void main(String[] args) {
//设置直接缓冲区
ByteBuffer directBuf = ByteBuffer.allocateDirect(10);
System.out.println("放入数据之前:position: "+directBuf.position()+",limit: "+directBuf.limit()+",capacity: "+directBuf.capacity());
//设置值
byte [] arry = {1,3,5,6,44,9,94,10,23};
directBuf.put(arry);
System.out.println("放入数据之后:position: "+directBuf.position()+",limit: "+directBuf.limit()+",capacity: "+directBuf.capacity());
//重新设置缓冲区
directBuf.flip();
System.out.println("执行 flip() 之前:position: "+directBuf.position()+",limit: "+directBuf.limit()+",capacity: "+directBuf.capacity());
//输出
System.out.println("子缓冲区的元素: ");
while (directBuf.hasRemaining()) {
System.out.print(directBuf.get()+"\t");
}
}
}
感觉和缓冲区一样,但是在性能上,使用直接缓冲区在理论上是提高程序性能。
四 总结
旧有的I/O操作,是操作硬盘的数据,效率低。使用NIO后,可以把数据读入,写入到缓冲区,缓冲区接近内存读取速度,所以会有显著的性能提高。NIO实现了除Boolean之外的所有基础数据类型的缓冲区实现,可以满足以往的方法修改,迁移。