Java開發筆記(九十二)文件通道的基本用法

前面介紹的各色流式IO在功能方面着實強大,處理文件的時候該具有的操做應有盡有,可流式IO在性能方面不盡如人意,它的設計原理使得實際運行效率偏低,爲此從Java4開始增長了NIO技術,經過全新的架構體系帶來了可觀的性能提高。
NIO是「Non-blocking IO」的縮寫,意思是非阻塞的IO,與之相對應,傳統的流式IO又被稱做BIO(「Blocking IO」的縮寫),意即阻塞的IO。所謂阻塞與非阻塞,提及來挺拗口,使人不知所云,這都是設計師腦殼短路惹的禍,發明了這麼難懂的詞彙,害得初學者一臉懵逼。其實阻塞與非阻塞的區別,猶如私家車與出租車的區別,私家車買回來之後只供車主一家開,沒開的時候要麼停在小區地庫,要麼停在公共停車場,其餘人是不能隨便坐上這部私家車的,如此一來私家車便處於阻塞模式,車門塞住了外人打不開。而出租車整日在街上穿行,有客人招手就停下來載客,開到目的地乘客下車,而後恢復空車狀態從新攬客,這樣出租車便處於非阻塞模式,車門沒塞住乘客打得開。顯然阻塞模式存在資源的極大浪費,一個資源分配給某人以後,即便無事可作也只能空在一邊閒得發慌;而非阻塞模式充分發揮了物盡其用的原則,一個資源用完以後立刻釋放,隨時容許下一我的接着使用。
非阻塞的NIO機制畫了一個高效的大餅,誰知對於文件來講倒是畫餅充飢,原來非阻塞模式只適用於網絡請求交互,而文件處理老是處於阻塞模式,想一想看,某個文件被A用戶打開以後,B用戶還能往該文件寫入數據嗎?很明顯即便A用戶打開文件後啥事都不作,B用戶也不能寫入該文件,緣於文件已經被A用戶霸佔了。之因此文件沒有非阻塞模式,是由於文件僅僅爲磁盤上的某個存儲片斷,它既不智能也不主動,更沒法進行任務調度,只能被動的打開和關閉。既然文件處理不支持非阻塞機制,難道NIO技術對文件來講形同虛設?固然事實並不是如此,要知道NIO技術不光光包括非阻塞機制,還包括文件通道、虛擬內存等等手段,可謂博大精深、不一而足。
先看文件通道,衆所周知,傳統的流式IO分爲輸入流與輸出流,輸入與輸出擁有各自的Stream工具,輸入流工具InputStream只能用來讀文件,輸出流工具OutputStream只能用來寫文件,兩者井水不犯河水。那若是打開文件以後,想要一下子讀一下子寫,輸入流和輸出流可得忙壞了,讀的時候招呼InputStream來個全套操做,寫的時候再招呼OutputStream來個全套操做,實在是勞民傷財。文件通道就不同,通道中的數據容許雙向流動,流進來意味着讀操做,流出去意味着寫操做,這樣文件的讀寫操做集中在文件通道里進行,大大節省了系統的資源開銷。在操做系統層面,通道是一種專職I/O操做的簡單處理器,它專門負責輸入輸出控制,使得CPU從繁瑣的I/O處理中解放出來,從而有效地提升整個系統的資源利用率。
文件通道對應的Java類型名叫FileChannel,它的建立方式主要有兩種,第一種要經過輸入輸出流,即調用輸入輸出流的getChannel方法獲取通道對象。好比下面代碼根據文件輸入流獲得了可讀的文件通道:html

			// 第一種方式:根據文件輸入流得到可讀的文件通道
			FileChannel channel1 = new FileInputStream(mFileName).getChannel();

 

又以下面代碼根據文件輸出流獲得了可寫的文件通道:數組

			// 第一種方式:根據文件輸出流得到可寫的文件通道
			FileChannel channel2 = new FileOutputStream(mFileName).getChannel();

 

第二種方式則要經過隨機文件工具,仍舊調用隨機文件工具的getChannel方法獲取通道對象。此時文件通道對象的構建代碼示例以下:緩存

			// 第二種方式:根據隨機訪問文件得到可讀的文件通道
			FileChannel channel1 = new RandomAccessFile(mFileName, "r").getChannel();
			// 第二種方式:根據隨機訪問文件得到可寫的文件通道
			FileChannel channel2 = new RandomAccessFile(mFileName,"rw").getChannel();

 

獲得文件通道對象以後,接着便能調用下列方法完成相應的文件處理動做:
isOpen:判斷文件通道是否打開。
size:獲取文件通道的大小(即文件長度)。
truncate:截斷文件大小到指定長度。
read:把文件通道中的數據讀到字節緩存。
write:往文件通道寫入字節緩存中的數據。
force:強制寫入磁盤,至關於緩存輸出流的flush方法。
close:關閉文件通道。
從以上方法列表可知,FileChannel至關於集成了FileInputStream和FileOutputStream,用起來更加方便。下面來個利用文件通道寫文件的代碼例子,同樣的簡潔明瞭:網絡

	// 經過文件通道寫入文件
	private static void writeChannel() {
		String str = "春眠不覺曉,到處聞啼鳥。\n夜來風雨聲,花落知多少。";
		// 根據文件輸出流得到可寫的文件通道。注意文件通道支持try(...)的自動關閉操做
		try (FileChannel channel = new FileOutputStream(mFileName).getChannel()) {
			// 生成字符串對應的字節緩存對象
			ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
			channel.write(buffer); // 往文件通道寫入字節緩存
			//channel.force(true); // 強制寫入磁盤,至關於輸出流的flush方法
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

再來利用文件通道讀文件的代碼例子,具體以下所示:架構

	// 經過文件通道讀取文件
	private static void readChannel() {
		// 根據文件輸入流得到可讀的文件通道。注意文件通道支持try(...)的自動關閉操做
		try (FileChannel channel = new FileInputStream(mFileName).getChannel()) {
			int size = (int) channel.size(); // 獲取文件通道的大小(即文件長度)
			// 分配指定大小的字節緩存
			ByteBuffer buffer = ByteBuffer.allocateDirect(size);
			channel.read(buffer); // 把文件通道中的數據讀到字節緩存
			buffer.flip(); // 把緩衝區從寫模式切換到讀模式。從緩衝區讀取數據以前,必須先調用flip方法
			byte[] bytes = new byte[size]; // 建立與文件大小相同長度的字節數組
			buffer.get(bytes); // 把字節緩存中的數據取到字節數組
			String content = new String(bytes); // 把字節數組轉換爲字符串
			System.out.println("content="+content);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

看來文件通道在讀文件過程當中也使用了緩存,總體的代碼流程同緩存輸入流BufferedInputStream相似,只不過與FileChannel搭配的字節緩存ByteBuffer用着不太順手,彆着急,後面的文章將細細道來ByteBuffer的詳細用法。dom



更多Java技術文章參見《Java開發筆記(序)章節目錄工具

相關文章
相關標籤/搜索