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類可用於檔案的壓縮和解壓縮。