Java NIO ( New IO )是從 Java 1.4 版本開始引入的一個新的 IO API ,
可以替代標準的 Java IO API 。NIO 與原來的 IO 有同樣的作用和目的,但是使用的方式完全不同, NIO 支持面向緩沖區(qū)的、基于通道的 IO 操作。 NIO 將以更加高效的方式進行文件的讀寫操作.
IO | NIO |
---|---|
面向流(StreamOriented) | 面向緩沖區(qū)(BufferOriented) |
阻塞IO(BlockingIO) | 非阻塞IO(NonBlockingIO) |
(無) | 選擇器(Selectors) |
Java NIO 系統(tǒng)的核心在于:通道 (Channel) 和緩沖區(qū)(Buffer) 。
通道表示打開到 IO 設(shè)備 ( 例如:文件、套接字 ) 的連接。若需要使用 NIO 系統(tǒng),需要獲取用于連接 IO 設(shè)備的通道以及用于容納數(shù)據(jù)的緩沖區(qū)。然后操作緩沖區(qū),對數(shù)據(jù)進行處理。
簡而言之, Channel 負責(zé)傳輸, Buffer 負責(zé)存儲
緩沖區(qū)( Buffer ):一個用于特定基本數(shù)據(jù)類型的容器。由 java.nio 包定義的,所有緩沖區(qū)都是 Buffer 抽象類的子類。
Java NIO 中的 Buffer 主要用于與 NIO 通道進行交互,數(shù)據(jù)是從通道讀入緩沖區(qū),從緩沖區(qū)寫入通道中的。
Buffer 就像一個數(shù)組,可以保存多個相同類型的數(shù)據(jù)。
根據(jù)數(shù)據(jù)類型不同 (boolean 除外 ) ,有以下 Buffer 常用子類:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
上述 Buffer 類 他們都采用相似的方法進行管理數(shù)據(jù),只是各自
管理的數(shù)據(jù)類型不同而已。都是通過如下方法獲取一個 Buffer對象
static XxxBuffer allocate(int capacity) : 創(chuàng)建一個容量為 capacity 的 XxxBuffer 對象
Buffer 中的重要概念:
? 容量 (capacity) : 表示 Buffer 大數(shù)據(jù)容量,緩沖區(qū)容量不能為負,并且創(chuàng)
建后不能更改。
? 限制 (limit) : 第一個不應(yīng)該讀取或?qū)懭氲臄?shù)據(jù)的索引,即位于 limit 后的數(shù)據(jù)
不可讀寫。緩沖區(qū)的限制不能為負,并且不能大于其容量。
? 位置 (position) : 下一個要讀取或?qū)懭氲臄?shù)據(jù)的索引。緩沖區(qū)的位置不能為
負,并且不能大于其限制
? 標記 (mark) 與重置 (reset) : 標記是一個索引,通過 Buffer 中的 mark() 方法
指定 Buffer 中一個特定的 position ,之后可以通過調(diào)用 reset() 方法恢復(fù)到這
個 position.
標記、位置、限制、容量遵守以下不變式:
0 <= mark <= position <= limit <= capacity
緩沖區(qū)存取數(shù)據(jù)的兩個核心方法:
mark() : 標記是一個索引,通過 Buffer 中的 mark() 方法
指定 Buffer 中一個特定的 position ,之后可以通過調(diào)用 reset() 方法恢復(fù)到這
個 position.
public void test1(){
String str = "abcde";
//1. 分配一個指定大小的緩沖區(qū)
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() 存入數(shù)據(jù)到緩沖區(qū)中
buf.put(str.getBytes());
System.out.println("-----------------put()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//3. 切換讀取數(shù)據(jù)模式
buf.flip();
System.out.println("-----------------flip()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//4. 利用 get() 讀取緩沖區(qū)中的數(shù)據(jù)
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst, 0, dst.length));
System.out.println("-----------------get()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//5. rewind() : 可重復(fù)讀
buf.rewind();
System.out.println("-----------------rewind()----------------");
System.out.println(buf.position());
System.out.println(buf.limit());
System.out.println(buf.capacity());
//6. clear() : 清空緩沖區(qū). 但是緩沖區(qū)中的數(shù)據(jù)依然存在,但是處于“被遺忘”狀態(tài)
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());
}
方法 | 描述 |
---|---|
Buffer clear() | 清空緩沖區(qū)并返回對緩沖區(qū)的引用 |
Buffer flip() | 將緩沖區(qū)的界限設(shè)置為當(dāng)前位置,并將當(dāng)前位置充值為0 |
int capacity() | 返回Buffer的capacity大小 |
boolean hasRemaining() | 判斷緩沖區(qū)中是否還有元素 |
int limit() | 返回Buffer的界限(limit)的位置 |
Buffer limit(intn) | 將設(shè)置緩沖區(qū)界限為n,并返回一個具有新limit的緩沖區(qū)對象 |
Buffer mark() | 對緩沖區(qū)設(shè)置標記 |
int position() | 返回緩沖區(qū)的當(dāng)前位置position |
Buffer position(int n) | 將設(shè)置緩沖區(qū)的當(dāng)前位置為n,并返回修改后的Buffer對象 |
int remaining() | 返回position和limit之間的元素個數(shù) |
Buffer reset() | 將位置position轉(zhuǎn)到以前設(shè)置的mark所在的位置 |
Buffer rewind() | 將位置設(shè)為為0,取消設(shè)置的mark |
Buffer 所有子類提供了兩個用于數(shù)據(jù)操作的方法 get() 與 put() 方法
? 獲取 Buffer 中的數(shù)據(jù)
get() :讀取單個字節(jié)
get(byte[] dst) :批量讀取多個字節(jié)到 dst 中
get(int index) :讀取指定索引位置的字節(jié) ( 不會移動 position)
放入數(shù)據(jù)到 Buffer 中
put(byte b) :將給定單個字節(jié)寫入緩沖區(qū)的當(dāng)前位置
put(byte[] src) :將 src 中的字節(jié)寫入緩沖區(qū)的當(dāng)前位置
put(int index, byte b) :將指定字節(jié)寫入緩沖區(qū)的索引位置 ( 不會移動 position)
字節(jié)緩沖區(qū)要么是直接的,要么是非直接的。如果為直接字節(jié)緩沖區(qū),則 Java 虛擬機會盡大努力直接在
此緩沖區(qū)上執(zhí)行本機 I/O 操作。也就是說,在每次調(diào)用基礎(chǔ)操作系統(tǒng)的一個本機 I/O 操作之前(或之后),
虛擬機都會盡量避免將緩沖區(qū)的內(nèi)容復(fù)制到中間緩沖區(qū)中(或從中間緩沖區(qū)中復(fù)制內(nèi)容)。
直接字節(jié)緩沖區(qū)可以通過調(diào)用此類的 allocateDirect() 工廠方法 來創(chuàng)建。此方法返回的 緩沖區(qū)進行分配和取消
分配所需成本通常高于非直接緩沖區(qū) 。直接緩沖區(qū)的內(nèi)容可以駐留在常規(guī)的垃圾回收堆之外,因此,它們對
應(yīng)用程序的內(nèi)存需求量造成的影響可能并不明顯。所以,建議將直接緩沖區(qū)主要分配給那些易受基礎(chǔ)系統(tǒng)的
本機 I/O 操作影響的大型、持久的緩沖區(qū)。一般情況下,最好僅在直接緩沖區(qū)能在程序性能方面帶來明顯好
處時分配它們。
直接字節(jié)緩沖區(qū)還可以通過 FileChannel 的 map() 方法 將文件區(qū)域直接映射到內(nèi)存中來創(chuàng)建。該方法返回
MappedByteBuffer 。 Java 平臺的實現(xiàn)有助于通過 JNI 從本機代碼創(chuàng)建直接字節(jié)緩沖區(qū)。如果以上這些緩沖區(qū)中的某個緩沖區(qū)實例指的是不可訪問的內(nèi)存區(qū)域,則試圖訪問該區(qū)域不會更改該緩沖區(qū)的內(nèi)容,并且將會在
訪問期間或稍后的某個時間導(dǎo)致拋出不確定的異常。
字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖區(qū)可通過調(diào)用其 isDirect() 方法來確定。提供此方法是為了能夠在
性能關(guān)鍵型代碼中執(zhí)行顯式緩沖區(qū)管理。
文件i/o的讀操作,會先向文件設(shè)備發(fā)起讀請求,然后驅(qū)動把請求要讀的數(shù)據(jù)讀取到文件的緩沖區(qū)中,這個緩沖區(qū)位于內(nèi)核,然后再把這個緩沖區(qū)中的數(shù)據(jù)復(fù)制到程序虛擬地址空間中的一塊區(qū)域中。
文件i/o的寫操作,會向文件設(shè)備發(fā)起寫請求,驅(qū)動把要寫入的數(shù)據(jù)復(fù)制到程序的緩沖區(qū)中,位于用戶空間,然后再把這個緩沖區(qū)的數(shù)據(jù)復(fù)制到文件的緩沖區(qū)中。
內(nèi)存映射文件,是把位于硬盤中的文件看做是程序地址空間中一塊區(qū)域?qū)?yīng)的物理存儲器,文件的數(shù)據(jù)就是這塊區(qū)域內(nèi)存中對應(yīng)的數(shù)據(jù),讀寫文件中的數(shù)據(jù),直接對這塊區(qū)域的地址操作,就可以,減少了內(nèi)存復(fù)制的環(huán)節(jié)。
所以說,內(nèi)存映射文件比起文件I/O操作,效率要高,而且文件越大,體現(xiàn)出來的差距越大。
通道( Channel ):由 java.nio.channels 包定義的。 Channel 表示 IO 源與目標打開的連接。
Channel 類似于傳統(tǒng)的“流”。只不過 Channel本身不能直接訪問數(shù)據(jù), Channel 只能與Buffer 進行交互。
Java 為 Channel 接口提供的最主要實現(xiàn)類如下
本地文件傳輸通道
FileChannel :用于讀取、寫入、映射和操作文件的通道
網(wǎng)絡(luò)數(shù)據(jù)傳輸?shù)耐ǖ?br/>DatagramChannel :通過 UDP 讀寫網(wǎng)絡(luò)中的數(shù)據(jù)通道
SocketChannel :通過 TCP 讀寫網(wǎng)絡(luò)中的數(shù)據(jù)。
ServerSocketChannel :可以監(jiān)聽新進來的 TCP 連接,對每一個新進來的連接都會創(chuàng)建一個 SocketChannel
獲取通道的一種方式是對支持通道的對象調(diào)用
getChannel() 方法。支持通道的類如下:
本地I/O
FileInputStream
FileOutputStream
RandomAccessFile
網(wǎng)絡(luò) I/O
DatagramSocket
Socket
ServerSocket
獲取通道的其他方式是使用 Files 類的靜態(tài)方法 newByteChannel() 獲取字節(jié)通道?;蛘咄ㄟ^通道的靜態(tài)方法 open() 打開并返回指定通道。
例如:
在 JDK 1.7 中的 NIO.2 針對各個通道提供了靜態(tài)方法 open()
//打開一個讀取的通道
FileChannel in = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.READ);
//打開一個寫的通道
FileChannel out = FileChannel.open(Paths.get("MyTest.java"),StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
在 JDK 1.7 中的 NIO.2 的 Files 工具類的 newByteChannel()
將 Buffer 中數(shù)據(jù)寫入 Channel
例如:inChannel.read(byteBuffer)
從 Channel 讀取數(shù)據(jù)到 Buffer
例如:outChannel.write(byteBuffer);
public static void main(String[] args) throws IOException {
//創(chuàng)建文件輸入輸入流
FileInputStream in = new FileInputStream("短發(fā).mp3");
FileOutputStream out = new FileOutputStream("短發(fā)2.mp3");
//文件輸入輸入流的getChannel()方法獲取通道
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
//獲取非直接緩沖區(qū)
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//將通道中的數(shù)據(jù)放入到緩沖區(qū)中
while (inChannel.read(byteBuffer) != -1) {
//切換讀取數(shù)據(jù)的模式
byteBuffer.flip();
//將緩沖區(qū)中的數(shù)據(jù)寫入通道中
outChannel.write(byteBuffer);
//清空緩沖區(qū)
byteBuffer.clear();
}
//釋放資源
in.close();
out.close();
inChannel.close();
outChannel.close();
}
public static void main(String[] args) throws IOException {
//通過文件通道的靜態(tài)方法,打開讀寫通道
//參1:通過Paths獲取源文件的路徑
//參2:操作模式 StandardOpenOption.READ 讀取模式
//打開讀取文件的通道
FileChannel in = FileChannel.open(Paths.get("短發(fā).mp3"), StandardOpenOption.READ);
//打開寫入的通道 模式要讀還要寫 StandardOpenOption.CREATE 意思是文件不存在就創(chuàng)建,如果存在就覆蓋
//StandardOpenOption.CREATE_NEW 意思是文件不存在就創(chuàng)建,如果存在就報錯
FileChannel out = FileChannel.open(Paths.get("短發(fā)2.mp3"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//操作內(nèi)存映射文件(也就是這個緩沖區(qū)在物理內(nèi)存中)
MappedByteBuffer inByteBuffer = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
MappedByteBuffer outByteBuffer = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
//直接對緩沖區(qū)進行讀寫操作
byte[] bytes = new byte[inByteBuffer.limit()];
inByteBuffer.get(bytes);
outByteBuffer.put(bytes);
//釋放資源
in.close();
out.close();
}
通道之間的數(shù)據(jù)傳輸 用的也是直接緩沖區(qū)的方式
public static void main(String[] args) throws IOException {
FileChannel in = FileChannel.open(Paths.get("E:\MyTest.txt"), StandardOpenOption.READ);
FileChannel out = FileChannel.open(Paths.get("D:\MyTestCopy.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//in.transferTo(0,in.size(),out);//把數(shù)據(jù)讀到 輸出通道中取 完成文件的復(fù)制
//或者使用輸出通道
out.force(false);
out.transferFrom(in,0,in.size()); //寫出數(shù)據(jù),寫出的數(shù)據(jù)從輸入通道中來
in.close();
out.close();
分散讀?。?Scattering Reads )是指從 Channel 中讀取的數(shù)據(jù)“分散”到多個Buffer緩沖區(qū)中
聚集寫入( Gathering Writes )是指將多個 Buffer緩沖區(qū) 中的數(shù)據(jù)“聚集”到 Channel 。
public static void main(String[] args) throws IOException {
RandomAccessFile in = new RandomAccessFile("E:\demo.txt", "rw");
RandomAccessFile out = new RandomAccessFile("E:\democopy.txt", "rw");
//獲取讀取通道
FileChannel inChannel = in.getChannel();
//創(chuàng)建多個緩沖區(qū)
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
//分散讀取到多個緩沖區(qū)中
ByteBuffer[] byteBuffers=new ByteBuffer[]{buffer1,buffer2};//把多個緩沖區(qū)放到一個大的數(shù)組中
long read = inChannel.read(byteBuffers);//把這個大的緩沖區(qū)傳進去
//當(dāng)然我們可以看看,每個緩沖區(qū)中讀入的數(shù)據(jù)
//byteBuffers[0].flip(); //切換到讀取模式 看一下第一個緩沖區(qū),讀入的100個字節(jié)
//byte[] array = byteBuffers[0].array();//把ByteBuffer轉(zhuǎn)換成字節(jié)數(shù)組
//String s = new String(array, 0, byteBuffers[0].limit());
//System.out.println(s);
//把每個緩沖區(qū),切換到讀取模式
for (ByteBuffer buffer : byteBuffers) {
buffer.flip();
}
//聚集寫入
FileChannel outChannel = out.getChannel();
outChannel.write(byteBuffers);
//釋放資源
inChannel.close();
outChannel.close();
}
方法 | 描述 |
---|---|
int read(ByteBufferdst**)** | 從Channel中讀取數(shù)據(jù)到ByteBuffer |
long read(ByteBuffer**[]dsts)** | 將Channel中的數(shù)據(jù)“分散”到ByteBuffer[] |
int write(ByteBuffersrc**)** | 將ByteBuffer中的數(shù)據(jù)寫入到Channel |
long write(ByteBuffer**[]srcs)** | 將ByteBuffer[]中的數(shù)據(jù)“聚集”到Channel |
long position() | 返回此通道的文件位置 |
FileChannel position(long p) | 設(shè)置此通道的文件位置 |
long size() | 返回此通道的文件的當(dāng)前大小 |
FileChannel truncate(long s) | 將此通道的文件截取為給定大小 |
void force(boolean metaData) | 強制將所有對此通道的文件更新寫入到存儲設(shè)備中 |
static long copy(InputStream in, Path target, CopyOption... options)
將所有字節(jié)從輸入流復(fù)制到文件。
static long copy(Path source, OutputStream out)
將從文件到輸出流的所有字節(jié)復(fù)制到輸出流中。
static Path copy(Path source, Path target, CopyOption... options)
將一個文件復(fù)制到目標文件。
Files 常用方法:
? Path copy(Path src, Path dest, CopyOption … how) : 文件的復(fù)制
? Path createDirectory(Path path, FileAttribute<?> … attr) : 創(chuàng)建一個目錄
? Path createFile(Path path, FileAttribute<?> … arr) : 創(chuàng)建一個文件
? void delete(Path path) : 刪除一個文件
? Path move(Path src, Path dest, CopyOption…h(huán)ow) : 將 src 移動到 dest 位置
? long size(Path path) : 返回 path 指定文件的大小
Files 常用方法:用于判斷
boolean exists(Path path, LinkOption … opts) : 判斷文件是否存在
boolean isDirectory(Path path, LinkOption … opts) : 判斷是否是目錄
boolean isExecutable(Path path) : 判斷是否是可執(zhí)行文件
boolean isHidden(Path path) : 判斷是否是隱藏文件
boolean isReadable(Path path) : 判斷文件是否可讀
boolean isWritable(Path path) : 判斷文件是否可寫
boolean notExists(Path path, LinkOption … opts) : 判斷文件是否不存在
public static <A extends BasicFileAttributes> A readAttributes(Path path,Class<A> type,LinkOption...
options) : 獲取與 path 指定的文件相關(guān)聯(lián)的屬性。
Files 常用方法:用于操作內(nèi)容
SeekableByteChannel newByteChannel(Path path, OpenOption…h(huán)ow) : 獲取與指定文件的連接,how 指定打開方式。
DirectoryStream newDirectoryStream(Path path) : 打開 path 指定的目錄
InputStream newInputStream(Path path, OpenOption…h(huán)ow): 獲取 InputStream 對象
OutputStream newOutputStream(Path path, OpenOption…h(huán)ow) : 獲取 OutputStream 對象
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準確進行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。
新聞名稱:JavaNIO-創(chuàng)新互聯(lián)
標題來源:http://muchs.cn/article44/csgiee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、服務(wù)器托管、自適應(yīng)網(wǎng)站、網(wǎng)站內(nèi)鏈、App設(shè)計、網(wǎng)站導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容