NIO入门系列之第3章:从理论到实践:NIO 中的读和写
3.1 ?概述
讀和寫是 I/O 的基本過(guò)程。從一個(gè)通道中讀取很簡(jiǎn)單:只需創(chuàng)建一個(gè)緩沖區(qū),然后讓通道將數(shù)據(jù)讀到這個(gè)緩沖區(qū)中。寫入也相當(dāng)簡(jiǎn)單:創(chuàng)建一個(gè)緩沖區(qū),用數(shù)據(jù)填充它,然后讓通道用這些數(shù)據(jù)來(lái)執(zhí)行寫入操作。
在本節(jié)中,我們將學(xué)習(xí)有關(guān)在Java 程序中讀取和寫入數(shù)據(jù)的一些知識(shí)。我們將回顧 NIO 的主要組件(緩沖區(qū)、通道和一些相關(guān)的方法),看看它們是如何交互以進(jìn)行讀寫的。在接下來(lái)的幾節(jié)中,我們將更詳細(xì)地分析這其中的每個(gè)組件以及其交互。
3.2 ?從文件中讀取
在我們第一個(gè)練習(xí)中,我們將從一個(gè)文件中讀取一些數(shù)據(jù)。如果使用原來(lái)的 I/O,那么我們只需創(chuàng)建一個(gè)FileInputStream 并從它那里讀取。而在 NIO中,情況稍有不同:我們首先從FileInputStream 獲取一個(gè)FileInputStream 對(duì)象,然后使用這個(gè)通道來(lái)讀取數(shù)據(jù)。
在 NIO 系統(tǒng)中,任何時(shí)候執(zhí)行一個(gè)讀操作,您都是從通道中讀取,但是您不是直接從通道讀取。因?yàn)樗袛?shù)據(jù)最終都駐留在緩沖區(qū)中,所以您是從通道讀到緩沖區(qū)中。
因此讀取文件涉及三個(gè)步驟:(1)從FileInputStream 獲取 Channel,(2) 創(chuàng)建 Buffer,(3) 將數(shù)據(jù)從 Channel 讀到 Buffer 中。
現(xiàn)在,讓我們看一下這個(gè)過(guò)程。
3.3 ?三個(gè)容易的步驟
第一步是獲取通道。我們從 FileInputStream 獲取通道:
FileInputStream fin = new FileInputStream( "readandshow.txt" ); FileChannel fc = fin.getChannel();下一步是創(chuàng)建緩沖區(qū):
ByteBuffer buffer = ByteBuffer.allocate( 1024 );最后,需要將數(shù)據(jù)從通道讀到緩沖區(qū)中,如下所示:
fc.read( buffer );
您會(huì)注意到,我們不需要告訴通道要讀多少數(shù)據(jù)到緩沖區(qū)中。每一個(gè)緩沖區(qū)都有復(fù)雜的內(nèi)部統(tǒng)計(jì)機(jī)制,它會(huì)跟蹤已經(jīng)讀了多少數(shù)據(jù)以及還有多少空間可以容納更多的數(shù)據(jù)。我們將在緩沖區(qū)內(nèi)部細(xì)節(jié)中介紹更多關(guān)于緩沖區(qū)統(tǒng)計(jì)機(jī)制的內(nèi)容。
// ReadAndShow package com.mzsx.nio; import java.io.*; import java.nio.*; import java.nio.channels.*; public class ReadAndShow {static public void main(String args[]) throws Exception {//創(chuàng)建FileInputStream實(shí)例FileInputStream fin = new FileInputStream("C:\\Users\\Qiang\\Desktop\\hydra.txt");//獲取通道FileChannel fc = fin.getChannel();//創(chuàng)建緩沖區(qū)ByteBuffer buffer = ByteBuffer.allocate(1024);//將數(shù)據(jù)從通道讀到緩沖區(qū)中fc.read(buffer);//反轉(zhuǎn)buffer.flip();int i = 0;while (buffer.remaining() > 0) {byte b = buffer.get();System.out.println("Character " + i + ": " + ((char) b)+" , buffer.remaining():"+buffer.remaining());i++;}fin.close();} }
3.4 ?寫入文件
在 NIO 中寫入文件類似于從文件中讀取。首先從 FileOutputStream 獲取一個(gè)通道:
FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" ); FileChannel fc = fout.getChannel();
下一步是創(chuàng)建一個(gè)緩沖區(qū)并在其中放入一些數(shù)據(jù)- 在這里,數(shù)據(jù)將從一個(gè)名為message 的數(shù)組中取出,這個(gè)數(shù)組包含字符串"Some bytes" 的 ASCII 字節(jié)(本教程后面將會(huì)解釋buffer.flip() 和 buffer.put() 調(diào)用)。
ByteBuffer buffer = ByteBuffer.allocate( 1024 ); for (int i=0; i<message.length; ++i) {buffer.put( message[i] ); } buffer.flip();
最后一步是寫入緩沖區(qū)中:
fc.write( buffer );注意在這里同樣不需要告訴通道要寫入多數(shù)據(jù)。緩沖區(qū)的內(nèi)部統(tǒng)計(jì)機(jī)制會(huì)跟蹤它包含多少數(shù)據(jù)以及還有多少數(shù)據(jù)要寫入。
// WriteSomeBytes package com.mzsx.nio; import java.io.*; import java.nio.*; import java.nio.channels.*; public class WriteSomeBytes {static private final byte message[] = { 83, 111, 109, 101, 32, 98, 121,116, 101, 115, 46 };static public void main(String args[]) throws Exception {//實(shí)例化FileOutputStreamFileOutputStream fout = new FileOutputStream("C:\\Users\\Qiang\\Desktop\\writesomebytes.txt");//獲取通道FileChannel fc = fout.getChannel();//獲取緩沖區(qū)ByteBuffer buffer = ByteBuffer.allocate(1024);//向緩沖區(qū)添加數(shù)據(jù)for (int i = 0; i < message.length; ++i) {buffer.put(message[i]);}buffer.flip();//寫入緩沖區(qū)fc.write(buffer);fout.close();} }3.5 ?讀寫結(jié)合
下面我們將看一下在結(jié)合讀和寫時(shí)會(huì)有什么情況。我們以一個(gè)名為 CopyFile.java 的簡(jiǎn)單程序作為這個(gè)練習(xí)的基礎(chǔ),它將一個(gè)文件的所有內(nèi)容拷貝到另一個(gè)文件中。CopyFile.java 執(zhí)行三個(gè)基本操作:首先創(chuàng)建一個(gè) Buffer,然后從源文件中將數(shù)據(jù)讀到這個(gè)緩沖區(qū)中,然后將緩沖區(qū)寫入目標(biāo)文件。這個(gè)程序不斷重復(fù)—讀、寫、讀、寫—直到源文件結(jié)束。
CopyFile 程序讓您看到我們?nèi)绾螜z查操作的狀態(tài),以及如何使用 clear() 和 flip() 方法重設(shè)緩沖區(qū),并準(zhǔn)備緩沖區(qū)以便將新讀取的數(shù)據(jù)寫到另一個(gè)通道中。
3.6 ?運(yùn)行 CopyFile 例子
實(shí)例讀寫結(jié)合:
// CopyFile import java.io.*; import java.nio.*; import java.nio.channels.*; public class CopyFile {static public void main(String args[]) throws Exception {//需要輸入源文件夾和目標(biāo)文件的全路徑if (args.length < 2) {System.err.println("Usage: java CopyFile infile outfile");System.exit(1);}String infile = args[0];String outfile = args[1];FileInputStream fin = new FileInputStream(infile);FileOutputStream fout = new FileOutputStream(outfile);//獲得通道FileChannel fcin = fin.getChannel();FileChannel fcout = fout.getChannel();//創(chuàng)建緩沖區(qū)ByteBuffer buffer = ByteBuffer.allocate(1024);while (true) {//清空緩沖區(qū)buffer.clear();//將數(shù)據(jù)從通道讀到緩沖區(qū)中int r = fcin.read(buffer);//檢查狀態(tài)if (r == -1) {break;}//反轉(zhuǎn)buffer.flip();//寫入fcout.write(buffer);}fcin.close();fcout.close();fin.close();fout.close();} }因?yàn)榫彌_區(qū)會(huì)跟蹤它自己的數(shù)據(jù),所以CopyFile 程序的內(nèi)部循環(huán) (inner loop) 非常簡(jiǎn)單,如下所示:
fcin.read( buffer ); fcout.write( buffer );
第一行將數(shù)據(jù)從輸入通道fcin 中讀入緩沖區(qū),第二行將這些數(shù)據(jù)寫到輸出通道 fcout 。
3.7 ?檢查狀態(tài)
下一步是檢查拷貝何時(shí)完成。當(dāng)沒(méi)有更多的數(shù)據(jù)時(shí),拷貝就算完成,并且可以在 read() 方法返回 -1 是判斷這一點(diǎn),如下所示:
int r = fcin.read( buffer ); if (r==-1) {break; }
3.8 ?重設(shè)緩沖區(qū)
最后,在從輸入通道讀入緩沖區(qū)之前,我們調(diào)用 clear() 方法。同樣,在將緩沖區(qū)寫入輸出通道之前,我們調(diào)用 flip() 方法,如下所示:
buffer.clear(); int r = fcin.read( buffer ); if (r==-1) {break; } buffer.flip(); fcout.write( buffer );
clear() 方法重設(shè)緩沖區(qū),使它可以接受讀入的數(shù)據(jù)。 flip() 方法讓緩沖區(qū)可以將新讀入的數(shù)據(jù)寫入另一個(gè)通道。
轉(zhuǎn)載于:https://blog.51cto.com/qiangmzsx/1409546
總結(jié)
以上是生活随笔為你收集整理的NIO入门系列之第3章:从理论到实践:NIO 中的读和写的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MemoryMappingFile泄漏分
- 下一篇: 用户软件互评