Java基礎——Java NIO詳解(一)

一、基本概念


1、I/0簡介

? ? ? ?I/O即輸入輸出,是計算機與外界世界的一個借口。IO操作的實際主題是操作系統。在java編程中,一般使用流的方式來處理IO,所有的IO都被視作是單個字節的移動,通過stream對象一次移動一個字節。流IO負責把對象轉換為字節,然后再轉換為對象。

? ? ? ?關于Java IO相關知識請參考我的另一篇文章:Java IO 詳解


2、什么是NIO

? ? ? ?NIO即New IO,這個庫是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實現方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多。

在Java API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網絡編程NIO,本篇文章重點介紹標NIO,關于網絡編程NIO請見Java NIO詳解(二)


3、流與塊的比較

? ? ? ?NIO和IO最大的區別是數據打包和傳輸方式。IO是以的方式處理數據,而NIO是以的方式處理數據。

? ? ? ?面向流的IO一次一個字節的處理數據,一個輸入流產生一個字節,一個輸出流就消費一個字節。為流式數據創建過濾器就變得非常容易,鏈接幾個過濾器,以便對數據進行處理非常方便而簡單,但是面向流的IO通常處理的很慢。

? ? ? ?面向塊的IO系統以塊的形式處理數據。每一個操作都在一步中產生或消費一個數據塊。按塊要比按流快的多,但面向塊的IO缺少了面向流IO所具有的有雅興和簡單性。


二、NIO基礎


? ? ? ?BufferChannel是標準NIO中的核心對象(網絡NIO中還有個Selector核心對象),幾乎每一個IO操作中都會用到它們。

Channel是對原IO中流的模擬,任何來源和目的數據都必須通過一個Channel對象。一個Buffer實質上是一個容器對象,發給Channel的所有對象都必須先放到Buffer中;同樣的,從Channel中讀取的任何數據都要讀到Buffer中。


1、關于Buffer

? ? ? ?Buffer是一個對象,它包含一些要寫入或讀出的數據。在NIO中,數據是放入buffer對象的,而在IO中,數據是直接寫入或者讀到Stream對象的。應用程序不能直接對 Channel 進行讀寫操作,而必須通過 Buffer 來進行,即 Channel 是通過 Buffer 來讀寫數據的。

? ? ? ?在NIO中,所有的數據都是用Buffer處理的,它是NIO讀寫數據的中轉池。Buffer實質上是一個數組,通常是一個字節數據,但也可以是其他類型的數組。但一個緩沖區不僅僅是一個數組,重要的是它提供了對數據的結構化訪問,而且還可以跟蹤系統的讀寫進程。

使用 Buffer 讀寫數據一般遵循以下四個步驟:

? ? ? ?1)寫入數據到 Buffer;

? ? ? ?2)調用 flip() 方法;

? ? ? ?3)從 Buffer 中讀取數據;

? ? ? ?4)調用 clear() 方法或者 compact() 方法。

? ? ? ?當向 Buffer 寫入數據時,Buffer 會記錄下寫了多少數據。一旦要讀取數據,需要通過 flip() 方法將 Buffer?從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到 Buffer 的所有數據。

? ? ? ?一旦讀完了所有的數據,就需要清空緩沖區,讓它可以再次被寫入。有兩種方式能清空緩沖區:調用 clear() 或 compact() 方法。clear() 方法會清空整個緩沖區。compact() 方法只會清除已經讀過的數據。任何未讀的數據都被移到緩沖區的起始處,新寫入的數據將放到緩沖區未讀數據的后面。

Buffer主要有如下幾種:



3、關于Channel

Channel是一個對象,可以通過它讀取和寫入數據。可以把它看做IO中的流。但是它和流相比還有一些不同:

? ? ? ?1)Channel是雙向的,既可以讀又可以寫,而流是單向的

? ? ? ?2)Channel可以進行異步的讀寫

? ? ? ?3)對Channel的讀寫必須通過buffer對象

? ? ? ?正如上面提到的,所有數據都通過Buffer對象處理,所以,您永遠不會將字節直接寫入到Channel中,相反,您是將數據寫入到Buffer中;同樣,您也不會從Channel中讀取字節,而是將數據從Channel讀入Buffer,再從Buffer獲取這個字節。

? ? ? ?因為Channel是雙向的,所以Channel可以比流更好地反映出底層操作系統的真實情況。特別是在Unix模型中,底層操作系統通常都是雙向的。


在Java NIO中Channel主要有如下幾種類型:

  • FileChannel:從文件讀取數據的
  • DatagramChannel:讀寫UDP網絡協議數據
  • SocketChannel:讀寫TCP網絡協議數據
  • ServerSocketChannel:可以監聽TCP連接


三、從理論到實踐:NIO中的讀和寫


? ? ? ?IO中的讀和寫,對應的是數據和Stream,NIO中的讀和寫,則對應的就是通道和緩沖區。NIO中從通道中讀取:創建一個緩沖區,然后讓通道讀取數據到緩沖區。NIO寫入數據到通道:創建一個緩沖區,用數據填充它,然后讓通道用這些數據來執行寫入。


1、從文件中讀取

? ? ? ?我們已經知道,在NIO系統中,任何時候執行一個讀操作,您都是從Channel中讀取,而您不是直接從Channel中讀取數據,因為所有的數據都必須用Buffer來封裝,所以您應該是從Channel讀取數據到Buffer。

因此,如果從文件讀取數據的話,需要如下三步:

? ? ? ?1)從FileInputStream獲取Channel

? ? ? ?2)創建Buffer

? ? ? ?3)從Channel讀取數據到Buffer

下面我們看一下具體過程:?

第一步:獲取通道

FileInputStream fin = new FileInputStream( "readandshow.txt" );
FileChannel fc = fin.getChannel(); 

第二步:建立緩沖區

ByteBuffer buffer = ByteBuffer.allocate( 1024 );
第三步:將數據從通道讀到緩沖區
fc.read( buffer );


2、寫入數據到文件
類似于從文件讀數據

第一步:獲取一個通道

FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
FileChannel fc = fout.getChannel();
第二步:創建緩沖區,將數據放入緩沖區

ByteBuffer buffer = ByteBuffer.allocate( 1024 );for (int i=0; i<message.length; ++i) {buffer.put( message[i] );
}
buffer.flip();
第三步:把緩沖區數據寫入通道中

fc.write( buffer );


3、讀寫結合
? ? ? ?CopyFile是一個非常好的讀寫結合的例子,我們將通過CopyFile這個實力讓大家體會NIO的操作過程。CopyFile執行三個基本的操作:創建一個Buffer,然后從源文件讀取數據到緩沖區,然后再將緩沖區寫入目標文件。

/*** 用java NIO api拷貝文件* @param src* @param dst* @throws IOException*/
public static void copyFileUseNIO(String src,String dst) throws IOException{//聲明源文件和目標文件FileInputStream fi=new FileInputStream(new File(src));FileOutputStream fo=new FileOutputStream(new File(dst));//獲得傳輸通道channelFileChannel inChannel=fi.getChannel();FileChannel outChannel=fo.getChannel();//獲得容器bufferByteBuffer buffer=ByteBuffer.allocate(1024);while(true){//判斷是否讀完文件int eof =inChannel.read(buffer);if(eof==-1){break;  }//重設一下buffer的position=0,limit=positionbuffer.flip();//開始寫outChannel.write(buffer);//寫完要重置buffer,重設position=0,limit=capacitybuffer.clear();}inChannel.close();outChannel.close();fi.close();fo.close();
}     


四、需要注意的點

上面程序中有三個地方需要注意


1、檢查狀態

? ? ? ?當沒有更多的數據時,拷貝就算完成,此時 read() 方法會返回 -1 ,我們可以根據這個方法判斷是否讀完。

int r= fcin.read( buffer );
if (r==-1) {break;
}


2、Buffer類的flip、clear方法

控制buffer狀態的三個變量

? ? ? ?1)position:跟蹤已經寫了多少數據或讀了多少數據,它指向的是下一個字節來自哪個位置

? ? ? ?2)limit:代表還有多少數據可以取出或還有多少空間可以寫入,它的值小于等于capacity。

? ? ? ?3)capacity:代表緩沖區的最大容量,一般新建一個緩沖區的時候,limit的值和capacity的值默認是相等的。

flip、clear這兩個方法便是用來設置這些值的。


flip方法

我們先看一下flip的源碼:

public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}

? ? ? ?在上面的FileCopy程序中,寫入數據之前我們調用了 buffer.flip();方法,這個方法把當前的指針位置position設置成了limit,再將當前指針position指向數據的最開始端,我們現在可以將數據從緩沖區寫入通道了。 position 被設置為 0,這意味著我們得到的下一個字節是第一個字節。 limit 已被設置為原來的 position,這意味著它包括以前讀到的所有字節,并且一個字節也不多。


clear方法

先看一下clear的源碼:

public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;
}

? ? ? ?在上面的FileCopy程序中,寫入數據之后也就是讀數據之前,我們調用了?buffer.clear();方法,這個方法重設緩沖區以便接收更多的字節。上圖顯示了在調用 clear() 后緩沖區的狀態。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446978.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446978.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446978.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MAC上Git安裝與GitHub基本使用

參考鏈接 MAC上Git安裝與GitHub基本使用

Java基礎——深入理解Java線程池

簡介 我們使用線程的時候就去創建一個線程&#xff0c;這樣實現起來非常簡便&#xff0c;但是就會有一個問題&#xff1a; 如果并發的線程數量很多&#xff0c;并且每個線程都是執行一個時間很短的任務就結束了&#xff0c;這樣頻繁創建線程就會大大降低系統的效率&#xff0c;…

密碼機項目安裝軟件時候出現的問題以及對應的解決辦法

Could NOT find Boost (missing: locale) (found version "1.65.1") 使用命令 apt-get install libboost-locale-dev 進行安裝 解決普通用戶cmake版本11&#xff0c;而root用戶版本15&#xff0c;clion對于版本兼容的問題 修改clion里面的toolchain&#xff0c;將其…

Java基礎——線程及并發機制

前言 在Java中&#xff0c;線程是一個很關鍵的名詞&#xff0c;也是很高頻使用的一種資源。那么它的概念是什么呢&#xff0c;是如何定義的&#xff0c;用法又有哪些呢&#xff1f;為何說Android里只有一個主線程呢&#xff0c;什么是工作線程呢。線程又存在并發&#xff0c;并…

密碼機 密鑰管理項目安裝配置 從零開始

安裝gcc 更新sudo apt-get update下載gcc sudo apt-get install gcc參考鏈接 不推薦 安裝g 下載g sudo apt-get install g 安裝make sudo apt -get install make參考鏈接 安裝cmake 下載地址參考鏈接 安裝ssh sudo apt-get install ssh 安裝git和配置 sudo apt-get inst…

Androud 如何有效減少重復代碼

前言 重復的代碼一直都是可維護性的大敵&#xff0c;重構的重要任務之一也就是要去除掉重復的代碼&#xff0c;有效的減少重復代碼&#xff0c;可以大大提高軟件的擴展性。 在Android開發中&#xff0c;很容易產生重復的代碼。因為Android是組件&#xff0c;模板式開發&#xf…

解決在sample文件夾里面寫代碼,在測試的時候因為virtual原因,make編譯報錯

代碼的結構 錯誤顯示 解決辦法 添加一句話&#xff0c;具體的cpp依據情況而定set_source_files_properties(${PROJECT_SOURCE_DIR}/src/sample_storage_test.cpp COMPILE_FLAGS "-Wno-unused-parameter")

Android SharedPreferences總結及優化

一、SharedPreferences簡介 Android 中的 SharedPreferences&#xff08;后續簡稱SP&#xff09;是輕量級的數據存儲方式&#xff0c;能夠保存簡單的數據類型&#xff0c;比如 String、int、boolean 值等。應用場合主要是數據比較少的配置信息。其內部是以 XML 結構保存在 /dat…

Java基礎——深入理解ReentrantLock

一、簡介在Java中通常實現鎖有兩種方式&#xff0c;一種是synchronized關鍵字&#xff0c;另一種是Lock。二者其實并沒有什么必然聯系&#xff0c;但是各有各的特點&#xff0c;在使用中可以進行取舍的使用。二、ReentrantLock與synchronized的比較相同點&#xff1a; &#xf…

使用開源的openssl的md5頭文件,實現對于文件的md5代碼

需要安裝openssl的庫 sudo apt-get install opensslsudo apt-get install libssl-dev參考鏈接 代碼 #include "openssl/md5.h" #include <iostream> #include <fstream> #include <iomanip>//#define MAX_DATA_BUFF 1024; //#define MD5_LENGTH…

Android 多進程開發

前言正常情況下&#xff0c;一個apk啟動后只會運行在一個進程中&#xff0c;其進程名為AndroidManifest.xml文件中指定的應用包名&#xff0c;所有的基本組件都會在這個進程中運行。但是如果需要將某些組件&#xff08;如Service、Activity等&#xff09;運行在單獨的進程中&am…

clion中鏈接openssl庫

錯誤顯示 前提條件 apt-get install opensslapt-get install openssl-dev 解決辦法 在CMakeLists.txt文件中加入如下命令link_libraries(crypto) 參考鏈接 無法將openssl庫鏈接到CLion C 程序c - 無法將openssl庫鏈接到CLion C程序

Java中String、StringBuffer、StringBuilder三者的區別

一、簡介String、StringBuffer、StringBuilder三個類之間的區別主要是在兩個方面&#xff1a;運行速度和線程安全。二、區別1、運行速度&#xff0c;或者說是執行速度在這方面運行速度快慢為&#xff1a;StringBuilder > StringBuffer > String StringString為字符串常量…

Ubuntu環境下,使用clion編譯器,使用開源opensll的對稱AES算法對于文件進行加密,C++代碼

前提準備條件 需要安裝openssl需要安裝openssl-dev需要配置CMakeLists.txt文件集體內容可以參考我提供的相關參考鏈接 AES_file.h #include <openssl/aes.h> #include <iostream> #include <fstream> #include <cstring>#define RELEASE_ARRAY(P) if…

Java提高篇 —— Java關鍵字之static的四種用法

一、前言 在java的關鍵字中&#xff0c;static和final是兩個我們必須掌握的關鍵字。不同于其他關鍵字&#xff0c;他們都有多種用法&#xff0c;而且在一定環境下使用&#xff0c;可以提高程序的運行性能&#xff0c;優化程序的結構。下面我們先來了解一下static關鍵字及其用法…

C++ 使用move來刪除用戶指定的文件

代碼 #include <iostream>bool remove_file(std::string path){if (remove(path.c_str())0){std::cout << "success!" << std::endl;}else{std::cout << "False!" << std::endl;} } int main() {std::string path "/…

Java提高篇 —— Java關鍵字之final的幾種用法

一、前言 在java的關鍵字中&#xff0c;static和final是兩個我們必須掌握的關鍵字。不同于其他關鍵字&#xff0c;他們都有多種用法&#xff0c;而且在一定環境下使用&#xff0c;可以提高程序的運行性能&#xff0c;優化程序的結構。下面我們來了解一下final關鍵字及其用法。 …

使用C++的方式實現AES算法

aes_file.h #include <iostream> #include <fstream> #include <bitset> #include <string> using namespace std; typedef bitset<8> byte; typedef bitset<32> word;const int Nr 10; // AES-128需要 10 輪加密 const int Nk 4; /…

Java提高篇 —— Java三大特性之封裝

一、封裝 封裝從字面上來理解就是包裝的意思&#xff0c;專業點就是信息隱藏&#xff0c;是指利用抽象數據類型將數據和基于數據的操作封裝在一起&#xff0c;使其構成一個不可分割的獨立實體&#xff0c;數據被保護在抽象數據類型的內部&#xff0c;盡可能地隱藏內部的細節&am…

sqlite3的backup和restore函數的使用

參考代碼 第一段這個親測可以使用 #include <sqlite3.h> #include <iostream> /* ** Perform an online backup of database pDb to the database file named ** by zFilename. This function copies 5 database pages from pDb to ** zFilename, then unlocks pD…