1. 程式人生 > >Java中I/O(輸入/輸出)的操作

Java中I/O(輸入/輸出)的操作

Java的I/O技術可以將資料儲存到文字檔案、二進位制檔案甚至是ZIP壓縮檔案中,以達到永久性儲存資料的要求。

1、流概述

流是一組有序的資料序列,根據操作的型別,可分為輸入流和輸出流兩種。I/O(Input/Output)流提供了一條通道程式,可以使用這條通道把源中的位元組序列送到目的地。雖然I/O流經常與磁碟檔案存取有關,但是程式的源和目的地也可以是鍵盤、滑鼠、記憶體或顯示器視窗等。

2、輸入/輸出流

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

2.1 輸入流

InputStream類是位元組輸入流的抽象類,是所有位元組輸入流的父類。該類中所有方法遇到錯誤時都會引發IOException異常。

InputStream類的具體層次結構如圖所示:

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

Reader類的具體層次結構如下圖所示:

2.2 輸出流

OutputStream類是位元組輸入流的抽象類,此抽象類是表示輸出位元組流的所有類的超類。OutputStream類中的所有方法均返回void,在遇到錯誤時會引發IOException異常。

OutputStream類的具體層次如圖所示:

Writer類是字元輸出流的抽象類,所有字元輸出類的實現都是它的子類。

Writer類的層次結構如下圖所示:

3、File類

File物件即可表示檔案,也可表示目錄,在程式中一個File物件可以代表一個檔案或目錄。利用它可用來對檔案或目錄及其屬性進行基本操作。

3.1 檔案的建立與刪除

(1)引入File類。

import java.io.File;

(2)構建一個檔案物件,通用以下3種構造方法來建立檔案物件。

File(String pathname)

File(String parent , String child)

File(File f , String child)

示例:在專案中建立類FileTest,在主方法中判斷檔案是否存在,如果該檔案存在則將其刪除,不存在則建立該檔案。

import java.io.*;

public class FileTest
{
	// 建立類FileTest
	public static void main(String[] args)
	{
		// 建立檔案物件
		File file = new File("word.txt");
		if (file.exists())
		{
			// 如果該檔案存在
			file.delete(); // 將檔案刪除
			System.out.println("檔案已刪除"); // 輸出的提示資訊
		} else
		{
			// 如果檔案不存在
			try
			{
				// try語句塊捕捉可能出現的異常
				file.createNewFile(); // 建立該檔案
				System.out.println("檔案已建立"); // 輸出的提示資訊
			} catch (Exception e)
			{
				// catch處理該異常
				e.printStackTrace(); // 輸出異常資訊
			}
		}
	}
}

3.2 獲取檔案資訊

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

getName():獲取檔案的名稱。

canRead():判斷檔案是否為可讀的。

canWrite():判斷檔案是否可被寫入。

exits():判斷檔案是否存在。

length():獲取檔案的長度(以位元組為單位)。

getPath():獲取檔案路徑。

getAbsolutePath():獲取檔案的絕對路徑。

getParent():獲取檔案的父路徑。

isFile():判斷檔案是否存在。

isDirectory():判斷檔案是否為一個目錄。

isHidden():判斷檔案是否為隱藏檔案。

lastModified():獲取檔案最後修改時間(返回值:long型別)。

示例:獲取檔案相關資訊。

import java.io.*;

public class FileTest
{
	public static void main(String[] args)
	{
		File file = new File("word.txt"); // 建立檔案物件
		if (file.exists())
		{ // 如果檔案存在
			String name = file.getName(); // 獲取檔名稱
			long length = file.length(); // 獲取檔案長度
			String path = file.getPath();// 獲取檔案路徑
			String absolutePath = file.getAbsolutePath();// 獲取檔案絕對路徑
			boolean hidden = file.isHidden(); // 判斷檔案是否是隱藏檔案
			System.out.println("檔名稱:" + name); // 輸出資訊
			System.out.println("檔案長度是:" + length);
			System.out.println("檔案路徑是:" + path);
			System.out.println("檔案絕對路徑是:" + absolutePath);
			System.out.println("該檔案是隱藏檔案嗎?" + hidden);
		} else
		{ // 如果檔案不存在
			System.out.println("該檔案不存在"); // 輸出資訊
		}
	}
}

4、檔案輸入/輸出流

4.1 FileInputStream與FileOutputStream類

FileInputStream類與FileOutputStream類都是用來操作磁碟檔案。如果使用者的檔案讀取需求比較簡單,則可以使用FileInputStream類。該類繼承自InputStream類。FileOutputStream類與FileInputStream類對應,提供了基本的檔案寫入能力。FileOutputStream類是OutoputStream類的子類。

示例:使用FileOutputStream類向檔案中寫入資訊,然後通過FileInputStream類將檔案中的資料讀取到控制檯上。

import java.io.*;

public class FileTest
{
	public static void main(String[] args)
	{
		// 建立檔案物件
		File file = new File("word.txt");

		// 檔案寫操作
		try
		{
			// 建立FileOutputStream物件
			FileOutputStream out = new FileOutputStream(file);

			// 建立byte型陣列
			byte[] msgBytes = "我有一隻小毛驢,我從來也不騎".getBytes();

			// 將陣列中的資訊寫入到檔案中
			out.write(msgBytes);

			// 關閉流
			out.close();
		} catch (Exception e)
		{
			e.printStackTrace();
		}

		// 檔案讀操作
		try
		{
			// 建立FileInputStream類物件
			FileInputStream in = new FileInputStream(file);

			// 建立Byte陣列
			byte[] byt = new byte[1024];

			// 從檔案中讀取資訊
			int len = in.read(byt);

			// 關閉流
			in.close();

			// 將檔案中的資訊輸出
			System.out.println("檔案中的資訊是:" + new String(byt, 0, len));

		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

說明:雖然Java在程式結束時自動關閉所有開啟的流,但是當使用完流後,顯示地關閉所有開啟的流仍是一個號習慣。一個被開啟的流有可能會用盡系統資源,這取決於平臺和實現。如果沒有將開啟的流關閉,當另一個程式試圖開啟另一個流時,可能會得不到需要的資源。

4.2 FileReader類和FileWriter類

使用FileOutputStream類向檔案中寫入資料與使用FileInputStream類從檔案中將內容讀出來,存在一點不足,即這兩個類都只提供了對位元組或位元組陣列的讀取方法。由於漢字在檔案中佔用兩個位元組,如果使用位元組流,讀取不好可能會出現亂碼現象。此時採用字元流Reader或Writer類即可避免這種現象。
FileReader、FileWriter字元流對應了FileInputStream、FileOutputStream類。FileReader流順序地讀取檔案,只要不關閉流,每次呼叫read()方法就順序地讀取源中其餘的內容,直到源的末尾或流被關閉。

示例:使用FileWriter類向檔案中寫入資訊,然後通過FileReader類將檔案中的資料讀取到控制檯上。

import java.io.*;

public class FileTest
{
	public static void main(String[] args)
	{
		// 建立檔案物件
		File file = new File("word.txt");

		// 檔案寫操作
		try
		{
			// 建立FileWriter物件
			FileWriter out = new FileWriter(file);

			// 建立字串內容
			String msgStr = "我有一隻小毛驢,我從來也不騎";

			// 將陣列中的資訊寫入到檔案中
			out.write(msgStr);

			// 關閉流
			out.close();
		} catch (Exception e)
		{
			e.printStackTrace();
		}

		// 檔案讀操作
		try
		{
			// 建立FileReader類物件
			FileReader in = new FileReader(file);

			// 建立char陣列
			char[] charArray = new char[1024];

			// 從檔案中讀取資訊
			int len = in.read(charArray);

			// 關閉流
			in.close();

			// 將檔案中的資訊輸出
			System.out.println("檔案中的資訊是:" + new String(charArray, 0, len));

		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

5、帶快取的輸入輸出流

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

5.1 BufferedInputStream類與BufferedOutputStream類

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

BufferedInputStream(InputStream in)。

BufferedInputStream(InputStream in, int size)。

使用BufferedOutputStream類輸出資訊和往OutputStream輸出資訊完全一樣,只不過BufferedOutputStream有一個flush()方法用來將快取區的資料強制輸出完。BufferedOutputStream類也有兩個構造方法:

BufferedOutputStream(OutputStream out)。

BufferedOutputStream(OutputStream out, int size)。

注意:flush()方法就是用於即使在快取區沒有滿的情況下,也將快取區的內容強制寫入到外設,習慣上稱這個過程為重新整理。flush()方法只對使用快取區的OutputStream類的子類有效。當呼叫close()方法時,系統在關閉流之前,也會將快取區中的資訊重新整理到磁碟檔案中。

5.2 BufferedReader與BufferedWriter類

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

BufferedReader類常用方法:

read():讀取單個字元。

readLine():讀取一個文字行,並將其返回為字串。若無資料可讀,則返回null。

在使用BufferedWriter類的Write()方法時,資料並沒有立刻被寫入至輸出流中,而是首先進入快取區中。如果想立刻將快取區中的資料寫入輸出流中,一定要呼叫flush()方法。

BufferedWriter類中的方法都是返回void,常用方法:

write(String s, int off, int len):寫入字串的某一部分。

flush() :重新整理該流的快取。

newLine():寫入一個行分隔符。

示例:向指定的檔案寫入資料,並通過BufferedReader類將檔案中的資訊分行顯示。

import java.io.*;

public class FileTest
{
	public static void main(String args[])
	{
		// 定義字串陣列
		String content[] = { "好久不見", "最近好嗎", "常聯絡" };
		File file = new File("word.txt"); // 建立檔案物件
		try
		{
			FileWriter fw = new FileWriter(file); // 建立FileWriter類物件
			// 建立BufferedWriter類物件
			BufferedWriter bufw = new BufferedWriter(fw);
			// 迴圈遍歷陣列
			for (int k = 0; k < content.length; k++)
			{
				bufw.write(content[k]); // 將字串陣列中元素寫入到磁碟檔案中
				bufw.newLine(); // 將陣列中的單個元素以單行的形式寫入檔案
			}
			bufw.close(); // 將BufferedWriter流關閉
			fw.close(); // 將FileWriter流關閉
		} catch (Exception e)
		{ // 處理異常
			e.printStackTrace();
		}
		try
		{
			FileReader fr = new FileReader(file); // 建立FileReader類物件
			// 建立BufferedReader類物件
			BufferedReader bufr = new BufferedReader(fr);
			String s = null; // 建立字串物件
			int i = 0; // 宣告int型變數
			// 如果檔案的文字行數不為null,則進入迴圈
			while ((s = bufr.readLine()) != null)
			{
				i++; // 將變數做自增運算
				System.out.println("第" + i + "行:" + s); // 輸出檔案資料
			}
			bufr.close(); // 將FileReader流關閉
			fr.close(); // 將FileReader流關閉
		} catch (Exception e)
		{ // 處理異常
			e.printStackTrace();
		}
	}
}

執行結果:

第1行:好久不見
第2行:最近好嗎
第3行:常聯絡

6、資料輸入輸出流

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

6.1 利用資料流類DataInputStream類讀二進位制檔案

(1)引用相關類

import java.io.FileInputStream;
import java.io.DataInputStream;

(2)構造一個數據輸入流物件

FileInputStream fis = new FileInputStream("D:\\MyImage.jpg");
DataInputStream dis = new DataInputStream(fis);

(3)利用資料輸入流類的方法讀取二進位制檔案的資料。

dis.readInt();   // 讀取出來的是整型
dis.readByte();  // 讀取出來的資料是Byte型別

(4)關閉資料輸入流

dis.close();  // 關閉資料輸入流

6.2 利用資料流類DataOutputStream類寫二進位制檔案

(1)引用相關類

import java.io.DataOutputStream;
import java.io.FileOutputStream;

(2)構造一個數據輸出流物件

FileOutputStream outFile = new FileOutputStream("E:\\MyImage2.jpg");
DataOutputStream out = new DataOutputStream(outFile);

(3)利用資料輸出流類的方法寫二進位制檔案的資料

out.write(1);   //把資料寫入二進位制檔案

(4)關閉資料輸出流

out.close();

示例:將D盤下的MyImage.jpg檔案複製到E盤下,並命名為MyImage2.jpg。

import java.io.FileInputStream;
import java.io.DataInputStream;

import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class ReadAndWriteBinaryFile
{

	public static void main(String[] args)
	{
		try
		{
			FileInputStream fis = new FileInputStream("D:\\MyImage.jpg");
			DataInputStream dis = new DataInputStream(fis);

			FileOutputStream outFile = new FileOutputStream("E:\\MyImage2.jpg");
			DataOutputStream out = new DataOutputStream(outFile);

			int temp;
			while ((temp = dis.read()) != -1)
			{
				out.write(temp);
			}

			dis.close();
			out.close();

			System.out.println("檔案複製成功");

		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}

7、ZIP壓縮輸入/輸出流

ZIP壓縮管理檔案(ZIP archive)是一種十分典型的檔案壓縮形式,使用它可以節省儲存空間。關於ZIP壓縮的I/O實現,在Java的內建類中提供了非常好用的相關類,所以其實實現方法非常簡單。使用java.util.zip包的ZipInputStream類與ZipOutputStream類來實現檔案的壓縮/解壓縮。利用ZipEntry、ZipInputStream和ZipOutputStream這3個Java類實現ZIP資料壓縮方式的程式設計方法。

7.1 壓縮檔案

利用ZipOutputStream類物件,可將檔案壓縮為“.zip”檔案。ZipOutputStream類的建構函式如下所示:

ZipOutputStream(OutputStream out);

ZipOutputStream類的常用方法:

putNextEntry(ZipEntry e):開始編寫新的ZIP檔案條目,並將流定位到條目資料的開頭。 

write(byte[] b, int off, int len):將一個位元組陣列寫入當前的ZIP條目資料。 

finish():完成編寫ZIP輸出流的內容,而不關閉底層流。 

setComment(String comment):設定ZIP檔案註釋。

示例:使用ZipOutputStream類對資料夾進行壓縮。

import java.io.*;
import java.util.zip.*;

public class MyZip { // 建立類
	private void zip(String zipFileName, File inputFile) throws Exception {
		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
				zipFileName)); // 建立ZipOutputStream類物件
		zip(out, inputFile, ""); // 呼叫方法
		System.out.println("壓縮中…"); // 輸出資訊
		out.close(); // 將流關閉
	}
	
	private void zip(ZipOutputStream out, File f, String base)
			throws Exception { // 方法過載
		if (f.isDirectory()) { // 測試此抽象路徑名錶示的檔案是否是一個目錄
			File[] fl = f.listFiles(); // 獲取路徑陣列
			out.putNextEntry(new ZipEntry(base + "/")); // 寫入此目錄的entry
			base = base.length() == 0 ? "" : base + "/"; // 判斷引數是否為空
			for (int i = 0; i < fl.length; i++) { // 迴圈遍歷陣列中檔案
				zip(out, fl[i], base + fl[i]);
			}
		} else {
			out.putNextEntry(new ZipEntry(base)); // 建立新的進入點
			// 建立FileInputStream物件
			FileInputStream in = new FileInputStream(f);
			int b; // 定義int型變數
			System.out.println(base);
			while ((b = in.read()) != -1) { // 如果沒有到達流的尾部
				out.write(b); // 將位元組寫入當前ZIP條目
			}
			in.close(); // 關閉流
		}
	}
	
	public static void main(String[] temp) { // 主方法
		MyZip book = new MyZip(); // 建立本例物件
		try {
			// 呼叫方法,引數為壓縮後文件與要壓縮檔案
			book.zip("hello.zip", new File("src"));
			System.out.println("壓縮完成"); // 輸出資訊
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

7.2 解壓ZIP檔案

ZipInputStream類可讀取ZIP壓縮格式的檔案,包括對已壓縮和未壓縮條目的支援(entry)。ZipInputStream類的建構函式如下所示:

ZipInputStream(InputStream in)

ZipInputStream的常用方法:

read(byte[] b, int off, int len):從當前ZIP條目讀取到位元組陣列。 

available():判斷是否已讀完目前entry所指定的資料。已讀完成返回0,否則返回1。

closeEntry():關閉當前的ZIP條目,並定位流以讀取下一個條目。 

skip(long n):跳過當前ZIP條目中指定的位元組數。 

getNextEntry():讀取下一個ZIP檔案條目,並將流定位在條目資料的開頭。 

createZipEntry(String name):為指定的條目名稱建立一個新的 ZipEntry物件。

示例:使用ZipInputStream類對檔案進行解壓縮。

import java.io.*;
import java.util.zip.*;

public class Decompressing { // 建立檔案
	public static void main(String[] temp) {
		ZipInputStream zin; // 建立ZipInputStream物件
		try { // try語句捕獲可能發生的異常
			zin = new ZipInputStream(new FileInputStream("hello.zip"));
			// 例項化物件,指明要進行解壓的檔案
			ZipEntry entry = zin.getNextEntry(); // 獲取下一個ZipEntry
			while (((entry = zin.getNextEntry()) != null)
					&& !entry.isDirectory()) {
				// 如果entry不為空,並不在同一目錄下
				File file = new File("d:\\" + entry.getName()); // 獲取檔案目錄
				System.out.println(file);
				if (!file.exists()) { // 如果該檔案不存在
					file.mkdirs();// 建立檔案所在資料夾
					file.createNewFile(); // 建立檔案
				}
				zin.closeEntry(); // 關閉當前entry
				System.out.println(entry.getName() + "解壓成功");
			}
			zin.close(); // 關閉流
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

總結:

1、流是指一連串流動的字元,是以先進先出的方式傳送資訊的通道。程式和資料來源之間是通過流聯絡起來的。

2、流可以分為輸入流和輸出流,也可以分為位元組流和字元流。

3、File類用於訪問檔案或目錄的屬性。

4、FileInputStream類和FileOutputStream類以位元組流的方式讀寫文字檔案。

5、BufferedReader類和BufferedWriter類以字元流的方式讀寫文字檔案,而且效率更高。

6、DataInputStream類和DataOutputStream類可用於讀寫二進位制檔案。

7、ZipInputStream類和ZipOutputStream類可用於檔案的壓縮和解壓縮。