NIO支持缓冲区和通道,效率非常高,非常好用,代码演示如下
1.NIO的HelloWorld

package cn.zzu.wcj.nio;

import static org.junit.Assert.*;

import java.nio.ByteBuffer;

import org.junit.Test;
/*
 * 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
 * 
 * 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
 * ByteBuffer
 * CharBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * FloatBuffer
 * DoubleBuffer
 * 
 * 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区
 * 
 * 二、缓冲区存取数据的两个核心方法:
 * put() : 存入数据到缓冲区中
 * get() : 获取缓冲区中的数据
 * 
 * 三、缓冲区中的四个核心属性:
 * capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
 * limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
 * position : 位置,表示缓冲区中正在操作数据的位置。
 * 
 * mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
 * 
 * 0 <= mark <= position <= limit <= capacity
 * 
 * 四、直接缓冲区与非直接缓冲区:
 * 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
 * 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
 */
public class BufferTest {

    @Test
    public void test1() {
         String str="abcde"   ;
         //1.分配一个指定大小的缓冲区
         ByteBuffer buf=ByteBuffer.allocate(1024)  ;
         System.out.println("-----------------allocate()------------------");
         System.out.println("position="+buf.position());
         System.out.println("limit="+buf.limit());
         System.out.println("capacity="+buf.capacity());
        //2.利用put()方法存入数据
         buf.put(str.getBytes())  ;
         System.out.println("-----------------put()------------------");
         System.out.println("position="+buf.position());
         System.out.println("limit="+buf.limit());
         System.out.println("capacity="+buf.capacity());
        //3.切换到读取模式
         buf.flip()   ;
         System.out.println("-----------------flip()------------------");
         System.out.println("position="+buf.position());
         System.out.println("limit="+buf.limit());
         System.out.println("capacity="+buf.capacity());
        //4.利用get()读取缓冲区中的数据
         byte[] dst=new byte[buf.limit()]  ;
         buf.get(dst,buf.position(),buf.limit())  ;
         System.out.println("-----------------get()------------------");
         System.out.println("position="+buf.position());
         System.out.println("limit="+buf.limit());
         System.out.println("capacity="+buf.capacity());
        //5.rewind()可重复读
         buf.rewind()   ;
         System.out.println("-----------------rewind()------------------");
         System.out.println("position="+buf.position());
         System.out.println("limit="+buf.limit());
         System.out.println("capacity="+buf.capacity());
        //6.clear():清空缓冲区,但是缓冲区中的数据依然存在,但是处于‘被遗忘’状态
         buf.clear()   ;
         System.out.println("-----------------clear()------------------");
         System.out.println("position="+buf.position());
         System.out.println("limit="+buf.limit());
         System.out.println("capacity="+buf.capacity());
         System.out.println((char)buf.get(0));


    }

    @Test
    public void test2(){
         String str="abcde"    ;   
         ByteBuffer buf=ByteBuffer.allocate(1024)   ;
         byte[] source=str.getBytes()   ;
         buf.put(source, 0,2)   ;
         System.out.println("position="+buf.position());
         buf.mark()  ;
         System.out.println("-------------mark()@2--------------");
         buf.put(source, 2, 2)   ;
         System.out.println("position="+buf.position());
         System.out.println("----------------reset()--------------------");
         buf.reset() ;
         System.out.println("position="+buf.position());
//       System.out.println("len="+source.length);
         if(buf.hasRemaining()){
             System.out.println(buf.remaining());
         }

    }

    @Test
    public void test3(){
          ByteBuffer buf=ByteBuffer.allocateDirect(1024)  ;
          assertSame(true, buf.isDirect());
    }

}

2.创建Channel的几种方式

package cn.zzu.wcj.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.junit.Test;
/*
 * 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
 * 
 * 二、通道的主要实现类
 *  java.nio.channels.Channel 接口:
 *      |--FileChannel
 *      |--SocketChannel
 *      |--ServerSocketChannel
 *      |--DatagramChannel
 * 
 * 三、获取通道
 * 1. Java 针对支持通道的类提供了 getChannel() 方法
 *      本地 IO:
 *      FileInputStream/FileOutputStream
 *      RandomAccessFile
 * 
 *      网络IO:
 *      Socket
 *      ServerSocket
 *      DatagramSocket
 *      
 * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
 * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
 * 
 * 四、通道之间的数据传输
 * transferFrom()
 * transferTo()
 * 
 * 五、分散(Scatter)与聚集(Gather)
 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
 * 
 * 六、字符集:Charset
 * 编码:字符串 -> 字节数组
 * 解码:字节数组  -> 字符串
 * 
 */
public class ChannelTest {

    @Test
    public void testEncDec() throws Exception{
          Charset charset = Charset.forName("GBK")   ;
          CharsetEncoder encoder = charset.newEncoder();
          CharsetDecoder decoder = charset.newDecoder()  ;
          CharBuffer charBuf=CharBuffer.allocate(1024)   ;
          charBuf.put("HelloWorld,世界你好!");
          charBuf.flip()  ;
          ByteBuffer byteBuffer = encoder.encode(charBuf);
          for(int x=0;x<byteBuffer.limit();x++){
              System.out.print(byteBuffer.get()+"、");
          }
          System.out.println();
          byteBuffer.flip()   ;
          CharBuffer charBuffer = decoder.decode(byteBuffer);
          System.out.println(charBuffer.toString());
          System.out.println("-----------------------------");
//          Charset charset2 = Charset.forName("UTF-8")   ;
          Charset charset2 = Charset.forName("GBK")   ;
          byteBuffer.flip()  ;
          CharBuffer charBuffer2 = charset2.decode(byteBuffer)  ;
          System.out.println(charBuffer2.toString());
    }

    @Test
    public void testCharset(){
        Map<String,Charset> charsets = Charset.availableCharsets()  ;
        Set<Entry<String,Charset>> set = charsets.entrySet()  ;
        for(Entry<String,Charset> e : set ){
            System.out.println(e.getKey()+"="+e.getValue());
        }
    }

    @Test
    public void testScatterAndGather()throws Exception{
          RandomAccessFile raf=new RandomAccessFile("1.txt", "rw")  ;
          FileChannel inChannel = raf.getChannel()  ;

          ByteBuffer buf=ByteBuffer.allocate(100)   ;
          ByteBuffer buf2=ByteBuffer.allocate(1024)   ;
          ByteBuffer bufs[]={buf,buf2}   ;

          inChannel.read(bufs)   ;
          for(ByteBuffer byteBuf : bufs){
              byteBuf.flip()  ;   //切换到读取模式
          }
          System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
          System.out.println("------------------------------------------------");
          System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));

          RandomAccessFile raf2=new RandomAccessFile("2.txt", "rw")  ;
          FileChannel outChannel = raf2.getChannel()  ;
          outChannel.write(bufs)  ;

          outChannel.close();   
          inChannel.close();
          raf.close();
          raf2.close();

    }

    @Test
    public void testChannel() throws Exception{
          long start=System.currentTimeMillis()   ;
          FileInputStream fis=null ;
          FileOutputStream fos=null ;

          fis=new FileInputStream("1.jpg");
          fos=new FileOutputStream("2.jpg") ;

          //1.获取通道
          FileChannel inChannel = fis.getChannel();
          FileChannel outChannel = fos.getChannel();

          //2.准备缓冲区
          ByteBuffer buf=ByteBuffer.allocate(1024)   ;

          //3.读写
          while(inChannel.read(buf) != -1){  //读
               buf.flip()   ;      //切换到读取模式
               outChannel.write(buf)  ;   //写
               buf.clear()   ;           //清空缓冲区,准备再次读取
          }

          //4.关闭流
          outChannel.close();  
          inChannel.close();   
          fos.close();   
          fis.close();   

          long end=System.currentTimeMillis()   ;
          System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒");         

    }

    @Test
    public void testDirectChannel()throws Exception{
           long start=System.currentTimeMillis()   ;
           FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)       ;
           FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ,
                                                                         StandardOpenOption.WRITE,
                                                                         StandardOpenOption.CREATE )    ;
           //内存映射文件
           MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size())   ;
           MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size())   ;

           //直接对缓冲区中的数据进行读写操作
           byte[] temp=new byte[1024]   ;   
           inMappedBuf.get(temp)   ;
           outMappedBuf.put(temp)    ;

           inChannel.close();   
           outChannel.close();   

           long end=System.currentTimeMillis()   ;
           System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒");        

    } 



    @Test
    public void testTransform()throws Exception{
        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)  ;
        FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"), StandardOpenOption.READ,
                                                                      StandardOpenOption.WRITE,
                                                                      StandardOpenOption.CREATE )    ;
        //inChannel.transferTo(0, inChannel.size(), outChannel)   ;
        outChannel.transferFrom(inChannel, 0, inChannel.size())   ;
        inChannel.close();
        outChannel.close();  
    }



}

3.阻塞式NIO

package cn.zzu.wcj.nio;



import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import org.junit.Test;
/*
 * 一、使用 NIO 完成网络通信的三个核心:
 * 
 * 1. 通道(Channel):负责连接
 *      
 *     java.nio.channels.Channel 接口:
 *          |--SelectableChannel
 *              |--SocketChannel
 *              |--ServerSocketChannel
 *              |--DatagramChannel
 * 
 *              |--Pipe.SinkChannel
 *              |--Pipe.SourceChannel
 * 
 * 2. 缓冲区(Buffer):负责数据的存取
 * 
 * 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
 * 
 */
public class BlockingNIOTest {

    @Test
    public void testClient() throws Exception {
         //1.创建通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
        //2.准备缓冲区
        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)  ;
        ByteBuffer buf=ByteBuffer.allocate(1024)   ;
        //3.读取本地文件,发送到客户端
        while(inChannel.read(buf) != -1){
             buf.flip()  ;
             socketChannel.write(buf)   ;
             buf.clear()  ;
        }
        //4.关闭通道
        inChannel.close();   
        socketChannel.close();  


    }

    @Test
    public void testServer() throws Exception{
        //1.创建通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()  ;
        //2.绑定端口号
        serverSocketChannel.bind(new InetSocketAddress(8989))   ;
        //3.准备Channel和Buffer
        FileChannel outChannel=FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE)  ;
        ByteBuffer buf=ByteBuffer.allocate(1024)   ;
        //4.接收客户端请求
        SocketChannel socketChannel = serverSocketChannel.accept()  ;
        while(socketChannel.read(buf) != -1){
            buf.flip()    ;
            outChannel.write(buf)   ;
            buf.clear()   ;
        }
        //5.关闭流
        socketChannel.close();  
        outChannel.close();   
        serverSocketChannel.close();
    }

}
package cn.zzu.wcj.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import org.junit.Test;

public class BlockingNIOTestPlus {

    @Test
    public void testClent() throws Exception{
        SocketChannel client = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999))   ;
        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)   ;
        ByteBuffer buf=ByteBuffer.allocate(1024)     ;
        while(inChannel.read(buf) != -1){
              buf.flip()    ;
              client.write(buf)    ;
              buf.clear()    ;
        }
        client.shutdownOutput()    ;    //结束输出

        //接收服务器反馈
        while(client.read(buf) != -1){
               buf.flip()    ;
               System.out.println(new String(buf.array(),0,buf.limit()));
               buf.clear()   ;
        }

        inChannel.close()  ;
        client.close()  ;  





    }

    @Test
    public void testServer() throws Exception{
        ServerSocketChannel server = ServerSocketChannel.open()   ;
        server.bind(new InetSocketAddress(9999))   ;
        FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), 
                                                  StandardOpenOption.WRITE,
                                                  StandardOpenOption.CREATE   )   ;
        ByteBuffer buf=ByteBuffer.allocate(1024)  ;
        SocketChannel client = server.accept();
        while(client.read(buf)!=-1){
              buf.flip()    ;
              outChannel.write(buf)    ;
              buf.clear()   ;
        }
        //发送反馈给客户端
        buf.put("乖儿子,爸爸接收到黄图啦!!!".getBytes())  ;
        buf.flip()   ;
        client.write(buf)   ;

        client.close();   
        outChannel.close();    
        server.close();    
    }


}

4.非阻塞式NIO

package cn.zzu.wcj.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

import org.junit.Test;

/*
 * 一、使用 NIO 完成网络通信的三个核心:
 * 
 * 1. 通道(Channel):负责连接
 *      
 *     java.nio.channels.Channel 接口:
 *          |--SelectableChannel
 *              |--SocketChannel
 *              |--ServerSocketChannel
 *              |--DatagramChannel
 * 
 *              |--Pipe.SinkChannel
 *              |--Pipe.SourceChannel
 * 
 * 2. 缓冲区(Buffer):负责数据的存取
 * 
 * 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
 * 
 */
public class TestNonBlockingChannel {

    @Test
    public void testClient() throws Exception{
        //创建客户端通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",6666))   ;
        //配置为非阻塞式NIO
        sChannel.configureBlocking(false)   ;
        //准备缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024)   ;
        Scanner sc=new Scanner(System.in)   ;
        while(sc.hasNext()){
            buffer.put((new Date().toString()+" : "+sc.next()).getBytes())    ;
            buffer.flip()  ;   //切换成读模式
            //向服务器端发送消息
            sChannel.write(buffer)  ;
            buffer.clear()    ;
        }
        //关闭通道
        sChannel.close();

    }


    @Test
    public void testServer() throws Exception{
        //1.创建服务器端通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open()   ;
        //2.绑定端口号
        ssChannel.bind(new InetSocketAddress(6666))    ;
        //3.设置非阻塞模式
        ssChannel.configureBlocking(false)    ;
        //4.获取选择器
        Selector selector = Selector.open()  ;
        //5.将选择器注册到通道上
        ssChannel.register(selector,SelectionKey.OP_ACCEPT)  ;
        //6.以轮训的方式获取选择器上已经准备就绪的事件
        while(selector.select() > 0){
            //7.接收全部选择键
             Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
             while(iterator.hasNext()){
                     //8.接收选择键
                     SelectionKey selectionKey = iterator.next();
                     //9.根据键值判断具体是什么事件
                     if(selectionKey.isAcceptable()){
                         //10.接收就绪,获取客户端连接
                         SocketChannel sChannel = ssChannel.accept()  ;
                         //11.切换到非阻塞模式
                         sChannel.configureBlocking(false)   ;
                         //12.将通道注册到选择器上
                         sChannel.register(selector, SelectionKey.OP_READ)   ;
                     }else if(selectionKey.isReadable()){
                         //13.获取读状态的通道
                         SocketChannel sChannel=(SocketChannel) selectionKey.channel()   ;
                         ByteBuffer dst=ByteBuffer.allocate(1024)  ;
                        //14.读取数据
                         Integer length=0  ;
                         while( (length=sChannel.read(dst))> 0){
                             dst.flip()    ;
                             System.out.println(new String(dst.array(),0,length));
                             dst.clear()   ;
                         }

                     }
                   //15.取消选择键
                     iterator.remove();  
             }

        }
    }



}
package cn.zzu.wcj.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

import org.junit.Test;

public class TestNonBlockingNIOPlus {

    @Test
    public void testSend() throws Exception{
       DatagramChannel dc=DatagramChannel.open()   ;
       dc.configureBlocking(false)    ;
       ByteBuffer buf=ByteBuffer.allocate(1024)    ;
       Scanner sc=new Scanner(System.in)  ;
       while(sc.hasNext()){
           String msg=sc.next()    ;
           buf.put((new Date().toString()+" : "+msg).getBytes())  ;
           buf.flip()   ;
           dc.send(buf, new InetSocketAddress("127.0.0.1",9999))  ;
           buf.clear()    ;
       }
       dc.close();  
    }


    @Test
    public void testReceive() throws Exception{
         DatagramChannel dc = DatagramChannel.open()  ;
         dc.configureBlocking(false)    ;
         dc.bind(new InetSocketAddress(9999))   ;
         Selector selector = Selector.open()  ;
         dc.register(selector, SelectionKey.OP_READ)   ;
         while(selector.select()>0){
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator()  ;
                while(iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isReadable()){
                        ByteBuffer buf=ByteBuffer.allocate(1024)  ;
                        dc.receive(buf)   ;
                        buf.flip()   ;
                        System.out.println(new String(buf.array(),0,buf.limit()));
                        buf.clear()  ;
                    }
                }
                iterator.remove();    
         }
    }




}

5.管道

package cn.zzu.wcj.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;
import java.nio.channels.Pipe.SinkChannel;
import java.nio.channels.Pipe.SourceChannel;

import org.junit.Test;

public class PipeTest {

    @Test
    public void testPipe() throws Exception{
         //1.获取管道
          Pipe pipe = Pipe.open()  ;
          SinkChannel sinkChannel = pipe.sink();
          //2.准备缓冲区
          ByteBuffer buf=ByteBuffer.allocate(1024)   ;
          buf.put("单向管道发送数据".getBytes())   ;
          buf.flip()   ;
          //4.发送数据
          sinkChannel.write(buf)   ;
          buf.clear()    ;
          //5.接收数据
          SourceChannel sourceChannel = pipe.source()  ;
          sourceChannel.read(buf)   ;
          buf.flip()  ;
          System.out.println(new String(buf.array(),0,buf.limit()));
    }

}