java.nio全稱java non-blocking(非阻塞) IO(其實是 new io),是指jdk1.4 及以上版本里提供的新api(New IO) ,爲全部的原始類型(boolean類型除外)提供緩存支持的數據容器,使用它能夠提供非阻塞式的高伸縮性網絡。java
IO | NIO |
---|---|
面向流(Stream Oriented) | 面向緩衝區(Buffer Oriented) |
阻塞IO(Blocking IO) | 非阻塞(Non Blocking IO) |
無 | 選擇器(Selectors) |
NIO系統的核心是:通道(Channel)和緩衝區(Buffer)api
位於 java.nio 包,全部緩衝區都是 Buffer 抽象類的子類,使用數組對數據進行緩衝。數組
除了 boolean 類型,Buffer 對每種基本數據類型都有針對的實現類:緩存
建立緩衝區經過 xxxBuffer.allocate(int capacity)方法網絡
ByteBuffer buf1 = ByteBuffer.allocate(512); LongBuffer buf2 = LongBuffer.allocate(1024); ……
容量(capacity):表示緩衝區存儲數據的最大容量,不能爲負數,建立後不可修改。app
限制:第一個不能夠讀取或寫入的數據的索引,即位於 limit 後的數據不能讀寫。不能爲負數,不能大於容量。dom
位置(position):下一個要讀取或寫入的數據的索引,位置不能爲負數,不能大於 limitpost
標記(mark):標記是一個索引,經過 Buffer 中的 mark() 方法指 Buffer 中一個特定的 position,以後能夠經過 reset() 方法回到這個 postion。性能
方法名稱 | 說明 |
---|---|
Buffer clear() | 清空緩衝區並返回對緩衝區的引用 |
Buffer flip() | 將緩衝區的 limit 設置爲當前位置,並將當前位置重置爲0 |
int capacity() | 返回 Buffer 的容量大小 |
boolean hasRemaining() | 判斷緩衝區是否還有元素 |
int limit() | 返回 限制的位置 |
Buffer limit(int n) | 將設置緩衝區界限爲 n,並返回一個具備新 limit 的緩衝區對象 |
Buffer mark() | 對緩衝區設置標記 |
int position() | 返回緩衝區的當前位置 position |
Buffer position(int n) | 將設置緩衝區的當前位置爲 n,並返回修改後的 Buffer 對象 |
int remaining() | 返回 position 和 limit 之間的元素個數 |
Buffer reset() | 將位置 position 轉到之前設置的 mark 所在的位置 |
Buffer rewind() | 將位置設置爲 0,取消設置的 mark |
Buffer 全部子類提供了兩個操做的數據的方法:get() 方法和 put() 方法code
緩衝區存取數據操做
package testnio; import java.nio.ByteBuffer; public class TestBuffer1 { public static void main(String[] args) { testuse(); } public static void testuse() { //1.分配一個指定大小的緩衝區 ByteBuffer buf=ByteBuffer.allocate(1024); System.out.println("---------------allocate()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //2.利用 put() 存入數據到緩衝區中 String str="hello"; //將字符串轉爲 byte 數組存入緩衝區 buf.put(str.getBytes()); System.out.println("---------------put()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //3.切換讀取數據模式 buf.flip(); System.out.println("---------------flip()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //4.利用get() 讀取緩衝區中的數據 byte[] data=new byte[buf.limit()]; System.out.println("---------------get()----------------"); buf.get(data); System.out.println(new String(data,0,data.length)); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //5.rewind() 重複讀 buf.rewind(); System.out.println("---------------rewind()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); //6.clear() 清空緩衝區,但緩衝區中的數據依然存在 buf.clear(); System.out.println("---------------clear()----------------"); System.out.println(buf.position()); System.out.println(buf.limit()); System.out.println(buf.capacity()); System.out.println((char)buf.get()); } }
使用 mark()方法標記
package testnio; import java.nio.ByteBuffer; public class TestBuffer2 { public static void main(String[] args) { testmark(); } public static void testmark() { String str="jikedaquan.com"; //建立緩衝區 ByteBuffer buf=ByteBuffer.allocate(1024); //存入數據 buf.put(str.getBytes()); //切換模式 buf.flip(); //臨時數組用於接收緩衝區獲取的數據,長度與緩衝區 limit 一值 byte[] data=new byte[buf.limit()]; //獲取緩衝區的數據從0開始獲取4個,存入 data 數組中 buf.get(data, 0, 4); //將數組轉爲字符串打印 System.out.println(new String(data,0,4)); //打印 position System.out.println(buf.position()); //標記 buf.mark(); System.out.println("---------------再次獲取----------------"); //從索引4開始,獲取6個字節(餘下數據) buf.get(data, 4, 6); System.out.println(new String(data,4,6)); System.out.println(buf.position()); //恢復到標記位置 buf.reset(); System.out.println("---------------reset()----------------"); System.out.println(buf.position()); //判斷緩衝區是是有還有剩餘數據 if (buf.hasRemaining()) { //獲取緩衝區中能夠操做的數量 System.out.println("可操做數量:"+buf.remaining()); } } }
mark <= position <= limit <= capacity
雖然使用了緩衝區提升了必定的IO速度,但這樣的效率仍然不是最高的。非直接緩衝區在與物理磁盤操做中須要通過內核地址空間copy操做,直接緩衝區不通過copy操做,直接操做物理內存映射文件,
使用直接緩衝區將大大提升效率。
直接緩衝區進行分配和取消分配所需成本工廠高於非直接緩衝區,通常狀況下,最好僅在直接緩衝區能在程序性能方面帶來明顯好處時分配它們。
直接緩衝區能夠經過調用此類的 allocateDirect()工廠方法建立
建立直接緩衝區
package testnio; import java.nio.ByteBuffer; public class TestBuffer3 { public static void main(String[] args) { testAllocateDirect(); } public static void testAllocateDirect() { //建立直接緩衝區 ByteBuffer buf=ByteBuffer.allocateDirect(1024); //是不是直接緩衝區 System.out.println(buf.isDirect()); } }
緩衝區僅是運載數據的容器,須要對數據讀寫還須要有一條通道,這二者是密不可分的。
Channel 接口的主要實現類:
支持通道的類:
使用通道和緩衝區實現文件讀和寫
package testnio; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class TestChannel { public static void main(String[] args) { FileInputStream fis=null; FileOutputStream fos=null; FileChannel inChannel=null; FileChannel outChannel=null; try { //建立輸入流 fis=new FileInputStream("F:/1.jpg"); //建立輸出流 fos=new FileOutputStream("F:/2.jpg"); //獲取通道 inChannel=fis.getChannel(); outChannel=fos.getChannel(); //分配指定大小的緩衝區 ByteBuffer buf=ByteBuffer.allocate(1024); //將通道中的數據存入緩存區 while(inChannel.read(buf)!=-1) { //切換讀取數據的模式 buf.flip(); //將讀入的緩衝區存入寫數據的管道 outChannel.write(buf); //清空緩存區(清空才能再次讀入) buf.clear(); } } catch (IOException e) { e.printStackTrace(); }finally { if (fis!=null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos!=null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
package testnio; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class TestOpenAndMapped { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try { //經過open建立通道 inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //內存映射文件 直接緩衝區 MappedByteBuffer inMappedBuf=inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMappedBuf=outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); //直接對緩衝區進行數據的讀寫操做 byte[] data=new byte[inMappedBuf.limit()]; inMappedBuf.get(data);//讀 outMappedBuf.put(data);//寫 } catch (IOException e) { e.printStackTrace(); }finally { if (inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
JDK 1.7 新增的方法 open(),參數 path 一般表明一個依賴系統的文件路徑,經過Paths.get()獲取。
參數 StandardOpenOption 是一個枚舉類型,經常使用值以下:
MappedByteBuffer:直接字節緩衝區,其內容是文件的內存映射區域。
將數據從源通道傳輸到其餘 Channel 中,transferTo() 和 transferFrom()
package testnio; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; public class TestChannelTransfer { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; try { //經過open建立管道 inChannel = FileChannel.open(Paths.get("F:/a.jpg"), StandardOpenOption.READ); outChannel = FileChannel.open(Paths.get("F:/b.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //將inChannel中全部數據發送到outChannel //inChannel.transferTo(0, inChannel.size(), outChannel); outChannel.transferFrom(inChannel, 0, inChannel.size()); } catch (IOException e) { e.printStackTrace(); }finally { if (inChannel!=null) { try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if (outChannel!=null) { try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
Channel 負責傳輸,Buffer 負責存儲