引言
Java IO(Input輸入/Output輸出)框架扮演着至關重要的角色,它是數據交換和文件處理的基石。
數據輸入到計算機內存的過程即輸入,反之輸出到外部存儲(比如數據庫,文件,遠程主機)的過程即輸出。數據傳輸過程類似於水流,因此稱為 IO 流。
一、Java IO體系概覽
Java IO 流有40多個類,他們都是從如下 4 個抽象類基類中派生出來的。
| 抽象基類 | 字節流 | 字符流 |
|---|---|---|
| 輸入流 | InputStream | Reader |
| 輸出流 | OutputStream | Writer |
字節流和字符流有什麼區別
- 字節流:以字節(8位)為單位進行數據的讀寫。它是計算機中最基本的數據傳輸單元,適用於處理包括文本、圖像、音頻、視頻等在內的二進制數據。
- 字符流:以字符為單位進行數據的讀寫。字符流在處理時,會根據指定的字符編碼(如UTF-8、GBK等)將字符轉換為字節進行處理。由於一個字符可能佔用多個字節(具體取決於編碼方式),主要用於處理文本數據。
IO 流的分類
| 類型 | 名稱 | 説明 |
|---|---|---|
| 按功能分 | 輸入流,輸出流 | 輸入流的類以InputStream, Reader為後綴。輸出流的類以OutputStream, Writer未後綴。 |
| 按照類型分 | 字節流、字符流 | 節點流的類以InputStream,OutputStream為後綴,字符流以Reader,Writer為後綴 |
| 按角色或功能層次 | 節點流(低級流、原始流)、包裝流(處理流、高級流) | 節點流 它是直接從數據源或目的地讀寫數據的流。除了對原數據進行讀取的節點流外,其他都是包裝流 |
下圖展示IO流系統圖-常用的類
二、IO操作之文件與目錄的操作
介紹File類,包括如何創建、刪除文件或目錄,檢查文件屬性等。
package file;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
public class studyOfFile {
@Test
public void createFile() throws IOException {
String filePath = "/Users/wu/file";
File file = new java.io.File(filePath);
/*創建文件*/
file.createNewFile();
/*查看文件的一下屬性*/
System.out.println("創建文件成功 ~");
System.out.println("文件名字: " + file.getName());
System.out.println("文件是否存在: " + file.exists());
System.out.println("文件是否是文件: " + file.isFile());
System.out.println("文件的路徑: " + file.getAbsolutePath());
System.out.println("文件是否擁有讀權限 " + file.canRead());
System.out.println("文件是否存在: " + file.exists());
/*刪除文件*/
file.delete();
System.out.println("刪除文件成功 ~");
System.out.println("文件是否存在: " + file.exists());
}
@Test
public void createDirectory() {
String directory = "/Users/wu/studyIo";
File file = new java.io.File(directory);
/*創建目錄*/
file.mkdir();
/*查看目錄的一下屬性*/
System.out.println("創建目錄成功 ~");
System.out.println("文件是否是文件: " + file.isFile());
System.out.println("文件是否是目錄: " + file.isDirectory());
/*刪除目錄*/
file.delete();
System.out.println("刪除目錄成功 ~");
System.out.println("目錄是否存在: " + file.exists());
}
}
文件操作執行結果:
目錄操作的執行結果:
三、以案例學習OI流
在本小節將展示一下內容:
字節流:FileInputStream和FileOutStream
字符流:FileReader和FileWiter
包裝流:BufferedInputStream
<-- FileReader和FileWiter的用法和下面的例子很類似,就不在寫demo了-->
FileInputStream例子
這個例子將演示如何使用FileInputStream來讀取文件內容,並使用FileOutputStream將讀取的內容寫入另一個文件。
package demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
String sourcePath = "/Users/wu/studyIo/source.txt";
String targetPath = "/Users/wu/studyIo/target.txt";
FileInputStream fis = null;
FileOutputStream fos = null;
try {
/*創建一個source.txt文件 用於從source.txt文件中讀取去數據*/
fis = new FileInputStream(sourcePath);
/*創建一個target.txt文件 用於將讀到的數據寫到target.txt文件中*/
fos = new FileOutputStream(targetPath);
/*設置緩衝區的大小 一次讀取1024字節*/
byte[] buffer = new byte[1024];
int length;
/*read方法放回讀取的字節數,讀取完畢返回-1*/
while ((length = fis.read(buffer)) > -1) {
/*邊讀邊寫*/
fos.write(buffer, 0, length);
}
System.out.println("文件複製完成。");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
/*記得關閉 釋放資源*/
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
/*真正執行寫的操作,否則寫入不成功*/
fos.write(0);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
這裏我遇到了一個問題:
fis = new FileInputStream(sourcePath);按理可以直接創建文件的。因為它的源代碼是這樣寫的
我會報錯
手動創建上兩個文件就不會報錯了。
至於為什麼不創建文件而報錯,我也沒想明白。
BufferedInputStream例子
這個例子將展示如何使用BufferedInputStream來提高文件讀取的效率。
package demo;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedStreamExample {
public static void main(String[] args) {
String sourcePath = "/Users/wu/studyIo/source.txt";
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(sourcePath));
int data = bis.read();
while (data != -1) {
System.out.print((char) data);
data = bis.read();
}
System.out.println("\n文件讀取完成。");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
運行結果:
這裏面會出現亂碼問題,原因是用的是BufferedInputStream他是字節流,一箇中文是佔3個字節,它將他分成了一個一個字節讀,就會亂碼,把BufferedInputStream換為BufferedReader字符流就可以解決亂碼問題。(要考慮文件編碼格式)
四、以對象處理流來學習序列化和反序列化
序列化:保持數據時,保持數據的值和數據類型
反序列化:恢復數據時,恢復數據的值和數據類型
package demo;
import java.io.*;
public class SerializationDemo {
public static void main(String[] args) {
String sourcePath = "/Users/wu/studyIo/employee.txt";
// 序列化過程
try (FileOutputStream fileOut = new FileOutputStream(sourcePath);
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
Employee emp = new Employee("John Doe", 30);
out.writeObject(emp);
System.out.println("Serialized data is saved in employee.ser");
} catch (IOException i) {
i.printStackTrace();
}
// 反序列化過程
Employee emp = null;
try (FileInputStream fileIn = new FileInputStream(sourcePath);
ObjectInputStream in = new ObjectInputStream(fileIn)) {
emp = (Employee) in.readObject();
System.out.println("Deserialized Employee...");
System.out.println("Employee : " + emp);
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
}
}
五、補充字符轉換流,打印流和隨機訪問流
補充流
六、IO異常處理
- 用try-catch-finally語句塊處理異常。
- 類名後面添加 throws IOException 聲明可以有IO異常發生,但不做處理。
參考資料
嗶哩嗶哩 -->韓順平老師講的IO專題視頻