1. 程式人生 > >Java NIO(二)通道Channel

Java NIO(二)通道Channel

一、通道(Channel):

用於源節點與目標節點的連線。在 Java NIO 中負責緩衝區中資料的傳輸。Channel 本身不儲存資料,因此需要配合緩衝區進行傳輸。


 二、通道的主要實現類
     java.nio.channels.Channel 介面:
         |--FileChannel:用於讀取、寫入、對映和操作檔案的通道。
         |--SocketChannel:通過 UDP 讀寫網路中的資料通道。
         |--ServerSocketChannel:通過 TCP 讀寫網路中的資料。
         |--DatagramChannel:可以監聽新進來的 TCP 連線,對每一個新進來的連線都會建立一個 SocketChannel。
 
 三、獲取通道
 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
  編碼:字串 -> 位元組陣列
  解碼:位元組陣列  -> 字串

    //非直接緩衝區,通道完成對檔案的複製
	@Test
	public void test01() throws Exception{
		FileInputStream in=new FileInputStream("f:/z6.png");
		FileOutputStream ou=new FileOutputStream("f:/2.jpg");
		//獲取通道
		FileChannel channeIn = in.getChannel();
		FileChannel channelOu = ou.getChannel();
		//分配指定大小緩衝區
		ByteBuffer allocate = ByteBuffer.allocate(1024);
		//將通道中的資料讀入緩衝區
		while(channeIn.read(allocate)!=-1){
			//切換讀取資料模式
			allocate.flip();
			//將緩衝區資料寫入通道
			channelOu.write(allocate);
			//清楚緩衝區
			allocate.clear();
		}
		channeIn.close();
		channelOu.close();
		in.close();
		ou.close();
	}

靜態方法 open()

    //使用直接緩衝區完成檔案的複製(記憶體對映檔案)
	@Test
	public void test02() throws IOException{
		FileChannel read = FileChannel.open(Paths.get("z6.png"), StandardOpenOption.READ);
		//StandardOpenOption.CREATE_NEW如果不存在則報錯,不進行覆蓋,create如果存在則進行覆蓋
		//Path path = Paths.get("e:/", "/s","/s");   可以進行路徑的拼接
		FileChannel write = FileChannel.open(Paths.get("2.png"), 
			StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
		//記憶體對映檔案
		MappedByteBuffer mapR = read.map(MapMode.READ_ONLY, 0, read.size());
		MappedByteBuffer mapW = write.map(MapMode.READ_WRITE, 0, read.size());
		byte[] dst=new byte[mapR.limit()];
		//直接對緩衝區進行資料的讀寫操作-因是直接緩衝區可以直接操作,不借用通道
		mapR.get(dst);
		mapW.put(dst);
		read.close();
		write.close();
	}

以上兩種方式,在使用1G大小的視訊進行復制測試的時候,使用直接緩衝區的速度明顯高於非直接緩衝區,但直接緩衝區有的時候會出現檔案複製完成,程式卻沒有結束的情況。直接緩衝區進行分配和取消分配所需成本通常高於非直接緩衝區 。直接緩衝區的內容可以駐留在常規的垃圾回收堆之外,因此,它們對應用程式的記憶體需求量造成的影響可能並不明顯。所以,建議將直接緩衝區主要分配給那些易受基礎系統的機 本機 I/O  操作影響的大型、持久的緩衝區。一般情況下,最好僅在直接緩衝區能在程式效能方面帶來明顯好
處時分配它們。

通道之間的資料傳輸

    //通道之間的資料傳輸,直接緩衝區
	@Test
	public void test03() throws Exception{
		FileChannel read = FileChannel.open(Paths.get("z6.png"), StandardOpenOption.READ);
		FileChannel write = FileChannel.open(Paths.get("7.png"), 
			StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
		//read.transferTo(0, read.size(), write);
		write.transferFrom(read, 0, read.size());
		read.close();
		write.close();
	}

分散(Scatter)與聚集(Gather)

    @Test
	public void test04() throws IOException{
		RandomAccessFile read=new RandomAccessFile("1.txt", "rw");//rw為讀寫操作簡寫 
		FileChannel channel = read.getChannel();//獲取通道
		ByteBuffer allocate1 = ByteBuffer.allocate(100);//分配緩衝區
		ByteBuffer allocate2 = ByteBuffer.allocate(10000);
		ByteBuffer dsts[]={allocate1,allocate2};
		channel.read(dsts);
		
		System.out.println(new String(dsts[0].array(), 0, dsts[0].limit()));
		System.out.println("==================");
		System.out.println(new String(dsts[1].array(), 0, dsts[1].limit()));
		//以上得出結論在寫入緩衝區的時候 ,是有序的,按順序操作
		//寫入
		for (ByteBuffer byteBuffer : dsts) {
		 byteBuffer.flip();
	    }
		RandomAccessFile write=new RandomAccessFile("2.txt", "rw");
		FileChannel channel2 = write.getChannel();
		channel2.write(dsts);
		channel2.close();
		channel.close();
		write.close();
		read.close();
	}

字符集:Charset

    @Test
	public void test05() throws IOException{
		//nio 支援的編碼
		/*SortedMap<String,Charset> availableCharsets = Charset.availableCharsets();
		Set<Entry<String,Charset>> entrySet = availableCharsets.entrySet();
		for (Entry<String, Charset> entry : entrySet) {
			System.out.println(entry.getKey()+"=="+entry.getValue());
		}*/
		Charset cs = Charset.forName("GBK");
		CharsetEncoder newEncoder = cs.newEncoder();//編碼
		CharsetDecoder newDecoder = cs.newDecoder();	//解碼
		CharBuffer cBuf = CharBuffer.allocate(1024);
		cBuf.put("我是中國人");
		cBuf.flip();
		ByteBuffer bBuf = newEncoder.encode(cBuf);//編碼
		for (int i = 0; i < bBuf.limit(); i++) {
			System.out.println(bBuf.get());
		}
		bBuf.flip();
		CharBuffer decode = newDecoder.decode(bBuf);	//解碼
		System.out.println("=========");
		System.out.println(decode.toString());
	}