NIO
JAVA NIO:称为非阻塞IO,读写的过程中不会发生阻塞线程
我们之前所学习的流,称为BIO,阻塞是IO,就是在读写的过程中可能会发生阻塞现象。
非阻塞IO面向Channel("通道")的,不是面向Stream(流)的。
流的特点:方向单一,顺序读写。流要么是输入流用于顺序读取数据,要么是输出流用于顺序写出数据
Channel的特点:双向的,既可以读又可以写。
JAVA NIO核心API:Channel 通道
常见的实现:
FileChannel:文件通道,可对文件进行读写操作
SocketChannel:套接字通道,可以与远端计算机进行TCP读写操作
ServerSocketChannel:服务端的套接字通道,用于监听客户端的连接
Buffer缓冲区
通道是对缓冲区中的数据进行读写操作
常见的缓冲区实现
ByteBuffer:字节缓冲区,缓冲区内部内容都是字节
//ByteBuffer支持一个静态方法,不用new:字节数组缓冲区,下面还有个buffer,不常用
缓冲区中重要的几个属性: position:当前位置,用来表示当前缓冲区已经有多少数据被操作了 limit:缓冲区最大可以操作的位置 capacity:容量,缓冲区的大小 默认创建一个缓冲区时: position=0 limit=capacity capacity=创建缓冲区时指定的大小
缓冲区的方法:
public abstract class Buffer {
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
}
缓冲区内部读写过程:
FileInputStream fis = new FileInputStream("movie.wmv");
FileChannel inChannel = fis.getChannel();//读取通道
//基于文件输入流获取一个用于读取该文件的文件通道
FileOutputStream fos = new FileOutputStream("movie_cp2.wmv");
FileChannel outChannel = fos.getChannel();//写出通道
//获取的输入流与输出流名字一样,因为这个通道同时有读取和写出的功能
//创建一个字节缓冲区
ByteBuffer buffer = ByteBuffer.allocate(14);//创建一个14字节大小的缓冲区
int len;//记录每次实际读取的数据量
/*
初始值:
position=0
limit=14
一次可以读取最多读取数据为:position到limit之间的数据量
limit-position = 14
*/
//实例1:只进行读取操作
//假设要读取的文件(movie.wmv)的大小为14字节
//读取前
System.out.println("读取前buffer状态=========================");
System.out.println("position:"+buffer.position());//0
System.out.println("limit:"+buffer.limit());//14
//第一次读取:
len = inChannel.read(buffer);//从通道中读取数据到缓冲区中
System.out.println("本次读取了:"+len+"个字节");//14
System.out.println("读取后buffer状态=========================");
System.out.println("position:"+buffer.position());//14
System.out.println("limit:"+buffer.limit());//14
//第二次读取
len = inChannel.read(buffer);
System.out.println("本次读取了:"+len+"个字节");//0
System.out.println("读取后buffer状态=========================");
System.out.println("position:"+buffer.position());//14
System.out.println("limit:"+buffer.limit());//14
/*
position=14
limit=14
一次可以读取最多读取数据为:position到limit之间的数据量
limit-position = 0
由于position的位置与limit一致,因此表示目前缓冲区已经没有空余可操作的空间了
*/
//实例2:复制操作(先读取后写出)
//假设要读取的文件(movie.wmv)的大小为16字节
//读取前
System.out.println("读取前buffer状态=========================");
System.out.println("position:"+buffer.position());//0
System.out.println("limit:"+buffer.limit());//14
len = inChannel.read(buffer);//第一次读取
//读取后:position:14,limit:14
outChannel.write(buffer);//此时写出字节个数为:0
//读取与写出操作都会影响到position的位置
//第一次读取后再写出发现写出字节数为0
//此时应用Buffer类的flip()和clear()方法
//实例3:复制文件:调用flip和clear方法,读取文件大小:16字节
len = inChannel.read(buffer);
//第一次读取了14个字节 position:14; limit:14
Buffer.flip();
//filp后,将position赋给limit,position=0 position:0; limit:14
outChannel.write(buffer);
//此时写出字节个数为:14 position:14; limit:14
Buffer.clear();
//clear后,limit=buffer最大容量,position=0 position:0; limit:14
len = inChannel.read(buffer);
//第二次读取了2个字节 position:2; limit:14
Buffer.flip();
//filp后,将position赋给limit,position=0 position:0; limit:2
outChannel.write(buffer);
//此时写出字节个数为:2 position:2; limit:2
Buffer.clear();
//clear后,limit=buffer最大容量,position=0 position:0; limit:14
总结:
Channel通道在进行读或写操作时,具体可以读取多少个字节或写出多少个字节是取决于
我们传入的Buffer中position到limit之间的空间。
文件复制
BIO文件复制
/*
*BIO的文件复制操作,使用流的方式进行复制
*/
FileInputStream fis = new FileInputStream("movie.wmv");
FileOutputStream fos = new FileOutputStream("movie_cp.wmv");
byte[] buffer = new byte[1024*10];//创建一个字节数组作为缓冲区
int len;//记录每次实际读取的字节数
while((len = fis.read(buffer))!=-1){
fos.write(buffer,0,len);
}
System.out.println("复制完毕");
fis.close();
fos.close();
NIO文件复制
/*
*NIO的文件复制操作
*/
FileInputStream fis = new FileInputStream("movie.wmv");
FileChannel inChannel = fis.getChannel();
FileOutputStream fos = new FileOutputStream("movie_cp2.wmv");
FileChannel outChannel = fos.getChannel();//同上
//创建一个字节缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024*10);//创建一个10k大小缓冲区
int len;//记录每次实际读取的数据量
//完整的复制动作
while((len = inChannel.read(buffer))!=-1){
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
过程分析: