NIO是在JDK1.4之后提供的新的API操作,其提高了效率。NIO主要有三大核心组件:Channel、Buffer、Selector,下面我们介绍前两个组件。
Buffer是一个抽象类,Buffer类型变量对应的对象代表一块缓冲区;
Channel是一个接口,该接口类型变量指向的对象代表一个数据传输通道;
Buffer、Channel、磁盘的关系大致如下图所示:
Channel通道会从磁盘上读取目标文件,然后将文件的数据传入Buffer缓存中,再经过另一条Channel通道将数据写入到磁盘上的目标地址。
下面来详细介绍一下这两个组件。
Buffer抽象类有许多子类,ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer类都是Buffer的子类,其中最常用的子类就是ByteBuffer,我们下面就以ByteBuffer举例。
该方法用于分配一个新的字节缓冲区,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
}
}
该方法返回此缓冲区的容量,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int size=byteBuffer.capacity();
System.out.println(size);
}
}
该方法将字节类型数据写入当前位置的缓冲区,然后当前位置+1,位置从0开始,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byte a=9;
byteBuffer.put(a);
}
}
该方法将ByteBuffer类型的数据转为byte数组,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byte [] array = byteBuffer.array();
}
}
该方法返回缓冲区当前位置,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byte a=9;
System.out.println(byteBuffer.position());
byteBuffer.put(a);
System.out.println(byteBuffer.position());
}
}
该方法翻转缓冲区,将position置零,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byte a=9;
System.out.println(byteBuffer.position());
byteBuffer.put(a);
System.out.println(byteBuffer.position());
byteBuffer.flip();
System.out.println(byteBuffer.position());
}
}
该方法用于判断缓冲区是否还有存储数据的空位,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
byte a=9;
byteBuffer.put(a);//position1
System.out.println(byteBuffer.hasRemaining());
byteBuffer.put(a);//position2
System.out.println(byteBuffer.hasRemaining());
}
}
该方法用于读取缓冲区当前位置的字节,然后当前位置+1,使用示例如下:
package earth;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocate(2);
byte a=9;
byteBuffer.put(a);
byteBuffer.flip();
System.out.println(byteBuffer.get());
}
}
该方法用于授权缓存中数据的覆盖,而且将位置归零,因为作用类似于清理,所以起名为clear,使用示例下面再说。
Channel对象是面向缓冲区的:数据总是从通道读取到缓冲区(Buffer类型对象),或从缓冲区(Buffer类型对象)写入到通道中。
Channel接口主要实现类主要有:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel,以下的示例使用FileChannel实现类。
由于接口不能创建对象,所以只能通过调用FileInputStream和FileOutputStream类中getChannel方法获取FileChannel对象。
下面我们来分析一个Channel和Buffer共同使用的简单示例:
第12行:创建一个输入流对象,传入源文件的路径。
第13行:调用输入流对象的getChannel方法创建一个输入通道类的对象inputChannel。
第14行:创建一个输出流对象,传入目标文件的路径。
第15行:调用输出流对象的getChannel方法创建一个输出通道类的对象outputChannel。
第17行:创建一个ByteBuffer类缓冲区,其大小为1024。
第19行:用输入通道调用read方法,将通道中自动从磁盘源文件中读取的数据传入byteBuffer缓冲区,此时该方法的返回值若为-1则说明读取到了文件结尾。
第20行:因为输入通道调用过read方法后,当前位置指向的是缓冲区的末尾,所以在从缓冲区提数据之前需要将当前位置改为缓冲区的开头。
第21行:用输出通道调用write方法将缓存区中的数据写到输出通道中,然后输出通道会自动将数据写入目标文件中。
第22行:因为每遍历一次都会从输入通道传来数据,所以每遍历一次结束后都需要将缓存区中的数据清空并将当前位置改为开头。
package earth;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class Test {
public static void main(String[] args) {
try {
FileInputStream fileInputStream = new FileInputStream("D:\\Program_Files\\useful\\笔记\\4、作业.wmv");
FileChannel inputChannel = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("D:\\Program_Files\\4.wmv");
FileChannel outputChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while(inputChannel.read(byteBuffer)!=-1) {
byteBuffer.flip();
outputChannel.write(byteBuffer);
byteBuffer.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}