IO流就是以流的方式進行輸入輸出。主要用來處理設備之間的傳輸,文件的上傳,下載和復制。
流分輸入和輸出,輸入流從文件中讀取數據存儲到進程中,輸出流從進程中讀取數據然后寫入到目標文件。
按照流的方向:輸入流(inputStream)和輸出流(outputStream)
按照實現功能分:節點流(可以從或向一個特定的地方(節點)讀寫數據。如 FileReader)和處理流(是對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀寫。如 BufferedReader。處理流的構造方法總是要帶一個其他的流對象做參數。一個流對象經過其他流的多次包裝,稱為流的鏈接。)
按照處理數據的單位: 字節流和字符流。字節流繼承于 InputStream 和 OutputStream, 字符流繼承于Reader 和 Writer 。
1)字節流讀取的時候,讀到一個字節就返回一個字節;字符流讀取的時候會讀到一個或多個字節(這個要根據字符流中編碼設置,一般中文對應的字節數是兩個,在UTF-8碼表中是3個字節)
2)字節流可以處理所有類型數據,如:圖片,MP3,AVI視頻文件,而字符流只能處理字符數據。只要是處理純文本數據,就要優先考慮使用字符流,除此之外都用字節流。
3)字節流在操作時本身不會用到緩沖區(內存),是文件本身直接操作的,而字符流在操作時使用了緩沖區,通過緩沖區再操作文件。
案例1:在寫操作的過程中,沒有關閉字節流操作,但是文件中也依然存在了輸出的內容代碼如下:
public static void main(String[] args) throws Exception {
// 第1步:使用File類找到一個文件
File f = new File("d:" + File.separator + "test.txt"); // 聲明File 對象
// 第2步:通過子類實例化父類對象
OutputStream out = new FileOutputStream(f);
// 第3步:進行寫操作
String str = "Hello World!!!"; // 準備一個字符串
byte b[] = str.getBytes(); // 字符串轉byte數組
out.write(b); // 將內容輸出
// 第4步:關閉輸出流
// out.close();
}
案例2:在寫操作的過程中,沒有關閉字符流操作,發現文件中沒有任何內容輸出。代碼如下:
public static void main(String[] args) throws Exception {
// 第1步:使用File類找到一個文件
File f = new File("d:" + File.separator + "test.txt");// 聲明File 對象
// 第2步:通過子類實例化父類對象
Writer out = new FileWriter(f);
// 第3步:進行寫操作
String str = "Hello World!!!"; // 準備一個字符串
out.write(str); // 將內容輸出
out.flush();
// 第4步:關閉輸出流
// out.close();
}
這是因為字符流操作時使用了緩沖區,而在關閉字符流時會強制性地將緩沖區中的內容進行輸出,但是如果程序沒有關閉,則緩沖區中的內容是無法輸出的。當然如果在不關閉字符流的情況下也可以使用Writer類中的flush()強制性的清空緩存,從而將字符流的內容全部輸出。
解題思路:把字節流轉成字符流就要用到適配器模式,需要用到OutputStreamWriter。它繼承了Writer接口,但要創建它必須在構造函數中傳入一個OutputStream的實例,OutputStreamWriter的作用也就是將OutputStream適配到Writer。它實現了Reader接口,并且持有了InputStream的引用。利用轉換流OutputStreamWriter.創建一個字節流對象,將其作為參數傳入轉換流OutputStreamWriter中得到字符流對象.
序列化是指把對象轉換為字節序列的過程,序列化后的字節流保存了對象的狀態以及相關的描述信息,從而方便在網絡上傳輸或者保存在本地文件中,達到對象狀態的保存與重建的目的。
反序列化:客戶端從文件中或網絡上獲得序列化后的對象字節流后,根據字節流中所保存的對象狀態及描述信息,通過反序列化重建對象。
序列化的優勢:一是實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上(通常存放在文件里),二是,利用序列化實現遠程通信,即在網絡上傳送對象的字節序列。三是通過序列化在進程間傳遞對象;
(1)java.io.ObjectOutputStream:表示對象輸出流;它的writeObject(Object obj)方法可以對參數指定的obj對象進行序列化,把得到的字節序列寫到一個目標輸出流中;
(2)java.io.ObjectInputStream:表示對象輸入流;它的readObject()方法源輸入流中讀取字節序列,再把它們反序列化成為一個對象,并將其返回;
注意:只有實現了Serializable或Externalizable接口的類的對象才能被序列化,否則拋出異常!
序列化和反序列化的示例
public class SerialDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
FileOutputStream fos = new FileOutputStream("object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
User user1 = new User("xuliugen", "123456", "male");
oos.writeObject(user1);
oos.flush();
oos.close();
//反序列化
FileInputStream fis = new FileInputStream("object.out");
ObjectInputStream ois = new ObjectInputStream(fis);
User user2 = (User) ois.readObject();
System.out.println(user2.getUserName()+ " " +
user2.getPassword() + " " + user2.getSex());
//反序列化的輸出結果為:xuliugen 123456 male
}
}
public class User implements Serializable {
private String userName;
private String password;
private String sex;
//全參構造方法、get和set方法省略
}
1. PrintStream 類的輸出功能非常強大,通常如果需要輸出文本內容,都應該將輸出流包裝成PrintStream 后進行輸出。它還提供其他兩項功能。與其他輸出流不同,PrintStream 永遠不會拋出 IOException;而是,異常情況僅設置可通過 checkError 方法測試的內部標志。另外,為了自動刷新,可以創建一個 PrintStream
2.BufferedWriter:將文本寫入字符輸出流,緩沖各個字符從而提供單個字符,數組和字符串的高效寫入。通過 write()方法可以將獲取到的字符輸出,然后通過 newLine()進行換行操作。BufferedWriter 中的字符流必須通過調用 flush 方法才能將其刷出去。并且 BufferedWriter 只能對字符流進行操作。如果要對字節流操作,則使用 BufferedInputStream
3.PrintWriter 的 println 方法自動添加換行,不會拋異常,若關心異常,需要調用 checkError方法看是否有異常發生,PrintWriter 構造方法可指定參數,實現自動刷新緩存(autoflush)。
因為明確說了是對字節流的讀取,所以肯定是InputStream或者他的子類,又因為要大量讀取,肯定要考慮到高效的問題,自然想到緩沖流BufferedInputStream。
原因:BufferedInputStream是InputStream的緩沖流,使用它可以防止每次讀取數據時進行實際的寫操作,代表著使用緩沖區。不帶緩沖的操作,每讀一個字節就要寫入一個字節,由于涉及磁盤的IO操作相比內存的操作要慢很多,所以不帶緩沖的流效率很低。帶緩沖的流,可以一次讀很多字節,但不向磁盤中寫入,只是先放到內存里。等湊夠了緩沖區大小的時候一次性寫入磁盤,這種方式可以減少磁盤操作次數,速度就會提高很多!并且也可以減少對磁盤的損傷。