java NIO(Channel)
1. NIO与IO的区别
NIO(New IO/Non-Blocking IO)是支持面向缓冲区、基于通道的IO操作。将以更高的效率进行文件的IO操作,可以代替原来的IO操作。
| IO | NIO | 
|---|---|
| 面向流 | 面向缓冲区 | 
| 阻塞IO | 非阻塞IO | 
| ———— | Selector(多路复用器、选择器) | 
2. 通道与缓冲区
通道是建立一个程序与资源之间的通路,即表示打开到IO设备(文件、套接字)的连接,不能通过通道直接操作资源(文件),NIO是以块来操作资源的,所以要借助缓冲区来实现。
通道负责传输,缓冲区负责存储数据,即当做数据的中转站。
3. 缓冲区
分为直接缓冲区,和间接缓冲区。
间接缓冲区:在JVM的堆上建立的缓冲区。
直接缓冲区:直接在系统内存上建立的缓冲区。
4. 通道基础
4.1. 通过与系统内存交互示意图

4.2. 通道的主要实现
- 
需要实现的接口: java.nio.channels.Channel
- 
声明: 
`public interface Channel
extends Closeable`
- 实现该接口的常用的类:
- 
磁盘IO:FileChannel 
- 
网络IO: 
- 
SocketChannel(普通套接字通道[TCP]) 
- 
ServerSocketChannel(服务器套接字通道[TCP]) 
- 
DatagramChannel (UDP套接字通道) 
5. 通道获取与使用
5.1. 获取通道
- 对支持通道的类提供getChannel方法
- 
本地IO:FileInputStream、FileOutputStream、RandomAccessFile 
- 
网络IO:Socket、ServerSocket、DatagramSocket 
- 各个通道提供了一个静态方法open
- 
static FileChannel open(Path path, OpenOption... options)
- 
static SocketChannel open(SocketAddress remote)/static SocketChannel open()
- 
static ServerSocketChannel open()
- 
static DatagramChannel open()/static DatagramChannel open(ProtocolFamily family)
- 通过Files工具类获取
- 
static SeekableByteChannel newByteChannel(Path path, OpenOption... options)
- 
其中SeekableByteChannel是FileChannel的父类 
5.2. 通道使用
利用通道完成文件复制
5.2.1. 利用间接缓冲区完成
public void tets1() throws Exception {
    //获取输入输出的通道
    FileChannel fin = FileInputStream("1.txt").getChannel();
    FileChannel fout = FileOutputStream("2.txt").getChannel();
    //分配指定大小的缓冲区,间接缓冲区
    ByteBuffer buf = ByteBuffer.allocate(1024);
    //将数据存入缓冲区
    while(fin.read(buf) != -1){
    //切换到读模式
    buf.flip();
    //写入
    fout.write(buf);
    //重置缓冲区,便于下次使用
    buf.clear();
    }
    //关闭通道
    fin.close();
    fout.close();
}
5.2.2. 利用内存映射文件完成(直接缓冲区)
直接缓冲区
public void test2() throws Exception {
    //获取通道
    FileChannel fin = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);
    FileChannel fout = FileChannel.open(Paths.get("2.txt"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    //内存映射
    MappedByteBuffer inBuf = fin.map(MapMode.READ_ONLY,0,fin.size());
    MappedByteBuffer oBuf = fout.map(MapMode.READ_WRITE,0,fin.size());
    //直接对缓冲区进行数据读写操作
    Byte[] dst = new Byte[inBuf.limit()];
    inBuf.get(dst);
    oBuf.put(dst);
    fin.close();
    fout.close();
}
5.2.3. 通道之间的数据传输
直接缓冲区
public void test3() throws Exception {
    FileChannel fin = FileChannel.open(Paths.get("1.txt"),StandardOpenOption.READ);
    FileChannel fout = FileChannel.open(Paths.get("2.txt"),StandardOpenOption.WRITE,StandardOpenOption.CREATE);
    //直接利用通道传输
    //fin.transformTo(0,fin.size(),fout);
    fout.transformFrom(fin,0,fin.size());
    fin.close();
    fout.close();
}
5.2.4. 分散(Scatter)与聚集(Gather)
- 
分散读取(Scattering Read):将通道中的数据按顺序分散到多个缓冲区 
- 
聚集写入(Gathering Write):将各个缓冲区的数据按顺序聚集到通道中 
public void test4() throws Exception{
    //获得随机访问文件
    RandomAccessFile file = new RandomAccessFile("1.txt","rw");
    //获得通道
    FileChannel fileChannel = file.getChannel();
    //获得指定大小的缓冲区
    ByteBuff buf1 = ByteBuff.allocate(100);
    ByteBuff buf2 = ByteBuffer.allacate(1024);
    ByteBuffer[] bufs = {buf1,buf2};
    //分散读到缓冲区
    fileChannel.read(bufs);
    //将每个缓冲区切换到读模式
    for(ByteBuffer buf : bufs){
        buf.flip();
    }
    System.out.println(new String(bufs[0],0,bufs[0].limit()));
    System.out.println("============================");
    System.out.println(new String(bufs[1],0,bufs[1].limit()));
    //聚集写入
    RandomAccessFile file2 = new RandomAccessFile("2.txt","rw");
    FileChannel fileChannel2 = file2.getChannel();
    fileChannel2.write(bufs);
    fileChannel.close();
    fileChannel2.close();
}