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

前言

在變量、數組和對象中存儲的數據是暫時存在的,程序結束後它們就會丟失。為瞭能夠永久地保存創建的數據,需要將其保存在磁盤文件中,這樣就可以在其他程序中使用它們。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輸入輸出內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: