16、New IO
1、为什么要使用NIO
NIO是JDK1.4加入发新包,NIO的创建目的是为了让java程序员可以实现高速I/O而无需编写自定义的本机代码。NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大的提高速度
2、流与块的比较
原来的I/O库与NIO最重要的区别是数据打包和传输的方式,原来的I/O以流的方式处理数据。而NIO以块的方式来处理数据
面向流的I/O系统一次一个字节的处理数据,一个输入流产生一个字节的数据,一个输出流消耗一个字节的数据,不利的一面是,面向流的I/O通常很慢
一个面向块的I/O系统以块的形式处理数据,每一个操作都作在下一步产生或者消耗一个数据块,按块处理数据比按字节处理数据要快很多,但是面向块的I/O缺少一下面向流的I/O所具有的优雅和简单。
3、缓冲区
在NIO中,所有数据都是用缓冲区处理的,在读取数据时,它直接读到缓冲区中,在写入数据时,她是写入到缓冲区中的,任何时候访问NIO中的数据,都是将它放入缓冲区中
缓冲区实质是一个数组,通常它是一个字节数组,但是也可以使用其他种类的数组,但是缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,而且可以跟踪系统的读写进程
4、缓冲区类型
最常用的缓冲区类型是ByteBuffer,一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的读取和设置)。ByteBuffer不是NIO中唯一的缓冲区类型,事实上每一种基本类型都有一个缓冲区类型
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
import java.nio.ByteBuffer;
public class NIODemo {
public static void main(String[] args) {
//创建一个字节缓冲区,申请内存空间为8字节allocate(分配)
ByteBuffer buf=ByteBuffer.allocate(8);
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
System.out.println("~~~~~~~~~~~~~");
//向缓冲区写入数据
buf.put((byte)10);
buf.put((byte)20);
buf.put((byte)30);
buf.put((byte)40);
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//缓冲区的反转
buf.flip();
System.out.println("~~~~~~~~~~~~~");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//hasRemaining告知当前位置和限制之间是否有元素
if(buf.hasRemaining()) {
//remaining返回当前位置与限制之间的元素数
for(int i=0;i<buf.remaining();i++) {
byte b=buf.get(i);
System.out.println(b);
}
}
System.out.println("~~~~~~~~~~~~~");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
}
}
position=0
limit=8
capacity=8
·············
position=4
limit=8
capacity=8
··········
position=0
limit=4
capacity=8
10
20
30
40
···········
position=0
limit=4
capacity=8
Buffer源码
5、通道:Channel
Channel是一个对象,可以通过它读取和写入数据,拿NIO和原来的I/O做一个比较,通道就是一个流。
正如前面踢到的,所有数据都通过Buffer对象来处理,您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区,同样,您不会 直接从通道中读取字节,而是将数据从通道读入缓冲区,再从很清楚获取这个字节
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import javax.swing.Icon;
/*
* 比较IO操作的性能比较
* 1、内存映射最快
* 2、NIO读写文件
* 3、使用缓冲的IO流
*
*/
public class CopyFileDemo {
private static void randomAccessFileCopy() throws Exception {
RandomAccessFile in=new RandomAccessFile("d:\\液化.jpg","r");
RandomAccessFile out=new RandomAccessFile("d:\\test\\液化.jpg","rw");
FileChannel fcIn=in.getChannel();
FileChannel fcOut=out.getChannel();
long size=fcIn.size();//输入流的字节大小
//输入流的缓冲区
MappedByteBuffer inBuf=fcIn.map(MapMode.READ_ONLY,0, size);
//输出流的缓冲区
MappedByteBuffer outBuf=fcOut.map(MapMode.READ_WRITE,0, size);
for(int i=0;i<size;i++) {
outBuf.put(inBuf.get());
}
//关闭通道是会写入数据块
fcIn.close();
fcOut.close();
in.close();
out.close();
System.out.println("copy success");
}
/*
* 通过文件通道实现文件的复制
*/
public static void copyFile() throws Exception {
//创建一个输入文件的通道
FileInputStream fis=new FileInputStream("d:\\液化.jpg");
FileChannel fcIn=fis.getChannel();
//创建一个输出文件的通道
FileOutputStream fos=new FileOutputStream("d:\\test\\液化.jpg");
FileChannel fcOut=fos.getChannel();
ByteBuffer buf=ByteBuffer.allocate(1024);
while(fcIn.read(buf)!=-1) {
//反转
buf.flip();
fcOut.write(buf);
buf.clear();
}
fcIn.close();
fcOut.close();
System.out.println("copy success");
}
public static void main(String[] args) {
try {
//copyFile();
randomAccessFileCopy();
} catch (Exception e) {
e.printStackTrace();
}
}
}