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());
}