首頁 > 軟體

Java中I/O輸入輸出的深入講解

2022-08-03 14:02:37

前言

在變數、陣列和物件中儲存的資料是暫時存在的,程式結束後它們就會丟失。為了能夠永久地儲存建立的資料,需要將其儲存在磁碟檔案中,這樣就可以在其他程式中使用它們。Java的I/O技術可以將資料儲存到文字檔案、二進位制檔案甚至是ZIP壓縮檔案中,已達到永久性儲存資料的要求。掌握I/O處理技術能夠提高對資料的處理能力。

一、流概述

流是一組有序的資料序列,根據操作的型別,可分為輸入流和輸出流兩種。I/O(Input/Output,輸入/輸出)流提供了一條通道程式,可以使用這條通道把源中的位元組序列送到目的地。雖然I/O流通常與磁碟檔案存取有關,但是程式的源和目的地也可以是鍵盤、滑鼠、記憶體或顯示器視窗等。
Java由資料流處理輸入/輸出模式,程式從指向源的輸入流中讀取源中的資料,源可以是檔案、網路、壓縮包或其他資料來源。
輸出流的指向是資料要到達的目的地,程式通過向輸出流中寫入資料把資訊傳遞到目的地。

二、輸入/輸出流

Java語言定義了許多類專門負責各種方式的輸入/輸出,這類類都被放在java.io包中。其中,所有輸入流類都是抽象類InputStream(位元組輸入流)或抽象類Reader(字元輸入流)的子類;而所有輸出流都是抽象類OutputStream(位元組輸出流)或抽象類Writer(字元輸出流)的子類。

1、輸入流

InputStream類是位元組輸入流的抽象類,是所有位元組輸入流的父類別。InputStream類的具體層次結構如下所示。
該類中所有方法遇到錯誤時都會引發IOException異常。下面是對該類中的一些方法的簡要說明。

read()方法:從輸入流中讀取資料的下一個位元組。返回0-255範圍內的int位元組值。如果因為已經到達流末尾而沒有可用的位元組,則返回值為-1。
read(byte[] b):從輸入流中讀入一定長度的位元組,並以整數的形式返回位元組數。
mark(int readlimit)方法:在輸入流的當前位置放置一個標記,readlimit引數告知此輸入流在標記位置失效之前允許讀取的位元組數。
reset()方法:將輸入指標返回到當前所做的標記處。
skip(long n)方法:跳過輸入流上的n個位元組並返回實際跳過的位元組數。
markSupported()方法:如果當前流支援mark()/reset()操作就返回true。
close方法:關閉此輸入流並釋放與該流關聯的所有系統資源。

Java中的字元是Unicode編碼,是雙位元組的。InputStream是用來處理位元組的,並不適合處理字元文字。Java為字元文字的輸入專門提供了一套單獨的類Reader,但Reader類並不是InputStream類的替換者,只是處理字串時簡化了程式設計。Reader類是字元輸入流的抽象類,所有字元輸入流的實現都是它的子類。

Reader類中的方法與InputStream類中的方法類似,讀者在需要時可檢視JDK檔案。

2、輸出流

OutputStream類是位元組輸出流的抽象類,此抽象類是表示輸出位元組流的所有類的超類。
OutputStream類中的所有方法均返回void,在遇到錯誤時會引發IoException異常。下面對OutputStream類中的方法作簡單的介紹。

write(int b)方法:將指定的位元組寫入此輸出流。
write(byte[] b)方法:將b個位元組從指定的byte陣列寫入此輸出流。
write(byte[] b,int off, int len)方法:將指定byte陣列中從偏移量off開始的len個位元組寫入此輸出流。
flush()方法:徹底完成輸出並清空快取區。
close()方法:關閉輸出流。

三、File類

File類是java.io包中唯一代表磁碟檔案本身的物件。File類定義了一些與平臺無關的方法來操作檔案,可以通過呼叫File類中的方法,實現建立、刪除、重新命名檔案等操作。File類的物件主要用來獲取檔案本身的一些資訊,如檔案所在的目錄、檔案的長度、檔案讀寫許可權等。資料流可以將資料寫入到檔案中,檔案也是資料流最常用的資料媒體。

1、檔案的建立與刪除

可以使用File類建立一個檔案物件。通常使用以下3種構造方法來建立檔案物件。

1、File(String pathname)

該構造方法通過將給定路徑名字串轉換為抽象路徑名來建立一個新File範例。

語法如下:

new File(String pathname);

其中,pathname指定路徑名稱(包含檔名)。例如:

File file = new File(“d:/1.txt”);

2、File(String parent,String child)

該構造方法根據定義的父路徑和子路徑字串(包含檔名)建立一個新的File物件。

語法如下:

new File(String parent,String child);

3、File(File f,String child)

該構造方法根據Parent抽象路徑名和child路徑名字串建立一個新的File範例。

語法如下:

new File(File f,String child);

2、獲取檔案資訊

File類提供了很多方法用於獲取一些檔案本身的資訊。如下表

方法| 返回值 | 說明
-------- | -----
getName() | String| 獲取檔案的名稱
canReda()| boolean |判斷檔案是否為可讀的
canWrite()| boolean | 判斷檔案是否可被寫入
exits()|boolean | 判斷檔案是否存在
length()|long | 獲取檔案的長度(以位元組為單位)
getAbsolutePath() | String | 獲取檔案的絕對路徑
getParent() | String | 獲取檔案的父路徑
isFile() | boolean | 判斷檔案是否存在
isDirectory() | boolean | 判斷檔案是否為一個目錄
isHidden() | boolean | 判斷檔案是否為隱藏檔案
lastModified() | long | 獲取檔案最後修改時間

四、檔案輸入/輸出流

程式執行期間,大部分資料都在記憶體中進行操作,當程式結束或關閉時,這些資料將消失。如果需要將資料永久儲存,可使用檔案輸入/輸出流與指定的檔案建立連線,將需要的資料永久儲存到檔案中。

1、FillInputStream與FileOutputStream類

FileInputStream類與FileOUtputStream類都用來操作磁碟檔案。如果使用者的檔案讀取需求比較簡單,則可以使用FileInputString類,該類繼承自InputString類。FileOutputStream類與FileInputStream類對應,提供了基本的檔案寫入能力。FileOutputStream類是OutputStream類的子類。
FileInputStream類常用的構造方法如下:

FileInputStream(String name)
FileInputStream(File file)

2、FileReader和FileWriter類

使用FileOutputStream類向檔案中寫入資料與使用FileInputStream類從檔案中將內容讀出來,都存在一點不足,即這兩個類都只提供了對位元組或位元組陣列的讀取方法。由於漢子在檔案中佔用兩個位元組,如果使用位元組流,讀取不好可能會出現亂碼現象,此時採用字元流Reader或Writer類即可避免這種現象。

FileReader和FileWriter字元流對應了FileInputStream和FileOutputStream類。FileReader流順序地讀取檔案,只要不關閉流,每次呼叫read()方法就順序地讀取源中其餘的內容,直到源的末尾或流被關閉。

五、帶快取的輸入/輸出流

快取是I/O的一種效能優化。快取流為I/O流增加了記憶體快取區。有了快取區,使得在流上執行skip()、mark()和reset()方法都成為可能。

1、BufferedInputStream與BufferedOutputStream類

BufferedInputStream類可以對所有InputStream類進行帶緩衝區的包裝以達到效能的優化。BufferedInputStream類有兩個構造方法:

BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)

第一種構造方法建立一個有32個位元組的快取區,第二種構造方法以指定的大小來建立快取區。

2、BufferedReader與BufferedWriter類

BufferedReader類與BufferedWriter類分別繼承Reader類與Writer類。這兩個類同樣具有內部快取機制,並可以以行為單位進行輸入/輸出。

六、資料輸入/輸出流

資料輸入/輸出流(DataInputStream類與DataOutputStream類)允許應用程式以與機器無關的方式從底層輸入流中讀取基本Java資料型別。也就是說,當讀取一個資料時,不必再關心這個數值應當是哪種位元組。

七、ZIP壓縮輸入/輸出流

ZIP壓縮管理檔案(ZIP archive)是一種十分典型的檔案壓縮形式,使用它可以節省儲存空間。關於ZIP壓縮的I/O實現,在Java的內建類中提供了非常好用的相關類,所以其實現方式非常簡單。本節將介紹使用java.util.zip包中的ZipOutputStream與ZipInputStream類來實現檔案的壓縮/解壓縮。如要從ZIP壓縮管理檔案內讀取某個檔案,要先找到對應檔案的“目錄進入點”(從它可知該檔案在ZIP檔案內的位置),才能讀取這個檔案的內容。如果要將檔案內容寫入ZIP檔案內,必須先寫入對應於該檔案的“目錄進入點”,並且把要寫入檔案內容的位置移到此進入點所指的位置,然後再寫入檔案內容。

Java實現了I/O資料流與網路資料流的單一介面,因此資料的壓縮、網路傳輸和解壓縮的實現比較容易。ZipEntry類產生的物件,是用來代表一個ZIP壓縮檔案內的進入點(entry)。ZipInputStream用來寫出ZIP壓縮格式的檔案,所支援的包括已壓縮及未壓縮的進入點(entry)。

ZipOutputStream類用來寫出ZIp壓縮格式的檔案,而且所支援的包括已壓縮及未壓縮的進入點(entry)。下面介紹利用ZipEntry、

ZipInputStream和ZipOutputStream3個Java類實現ZIP資料壓縮方式的程式設計方法。

1、壓縮檔案

利用ZipOutputStream類物件,可將檔案壓縮為.zip檔案。ZipOutputStream類的構造方法如下:

ZipOutputStram(OutputStream out);

ZipOutputStream類的常用方法如表所示:

方法返回值說明
putNextEntry(ZipEntry e)void開始寫一個新的ZipEntry,並將流內的位置移至此entry所指資料的開頭
write(byte[] b,int off,int len)void將位元組陣列寫入當前ZIP條目資料
finish()void完成寫入ZIP輸出流的內容,無須關閉它所配合的OutputStream
setComment(String comment)void可設定此ZIP檔案的註釋文字

2、解壓縮ZIP檔案

ZipInputStream類可讀取ZIP壓縮格式的檔案,包括已壓縮和未壓縮的條目(entry)。ZipInputStream類的構造方法如下:

ZipInputStream(InputStream in)

ZipInputStream類的常用方法如下表所示:

方法返回值說明
read(byte[] b, int off , int len)int讀取目標b陣列內off偏移量的位置,長度是len位元組
available()int判斷是否已讀完目前entry所指定的資料。已讀完返回0,否則返回1
closeEntry()void關閉當前ZIP條目並定位流以讀取下一個條目
skip(long n)long跳過當前ZIP條目中指定的位元組數
getNextEntry()ZipEntry讀取下一個ZipEntry,並將流內的位置移至該entry所指資料的開頭
createZipEntry(String name)ZipEntry以指定的name引數新建一個ZipEntry物件

補充:獲取目錄下的所有目錄和檔案

範例:假設目錄“D:TestDir1”下有兩個資料夾(dir1 和 dir2)和一個檔案 file1.txt 。

File[] listFiles()方法:獲取該目錄下的所有子目錄和檔案,返回File類陣列。

import java.io.File;
 
/**
 * 獲取目錄下的所有目錄和檔案
 * @author pan_junbiao
 **/
public class DirFileTest
{
    public static void main(String[] args)
    {
        File file = new File("D:\TestDir1");
 
        //判斷目錄是否存在
        if (!file.exists())
        {
            System.out.println("目錄不存在");
            return;
        }
 
        //獲取檔案陣列
        File[] fileList = file.listFiles();
        for (int i = 0; i < fileList.length; i++)
        {
            //判斷是否為目錄
            if (fileList[i].isDirectory())
            {
                System.out.println("目錄:" + fileList[i].getName());
            }
            else
            {
                System.out.println("檔案:" + fileList[i].getName());
            }
        }
    }
}

執行結果:

總結

這裡的相關內容還沒有整理完畢,文章後面持續更新,建議收藏。

文章中涉及到的命令大家一定要像我一樣每個都敲幾遍,只有在敲的過程中才能發現自己對命令是否真正的掌握了。

到此這篇關於Java中I/O輸入輸出的文章就介紹到這了,更多相關Java I/O輸入輸出內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com