JAVA IO流進階指南:字符流與字節(jié)流的深度應用
1.1 本章學習目標與重點
?? 掌握字節(jié)流與字符流的核心區(qū)別,能夠根據實際開發(fā)場景選擇合適的IO流實現(xiàn)文件操作。
?? 熟練運用緩沖流提升IO操作效率,解決大文件讀寫的性能問題。
?? 理解轉換流的作用,處理不同編碼格式的文件讀寫,避免亂碼問題。
?? 本章重點是流的嵌套使用和資源釋放的標準寫法,這是實際開發(fā)中高頻考點和易錯點。
1.2 字節(jié)流與字符流的核心差異(七千字以上內容展開)
1.2.1 基本概念與設計初衷
?? 字節(jié)流以byte為基本單位進行數(shù)據傳輸,它可以處理所有類型的文件,比如圖片、視頻、音頻、文本等。
字符流以char為基本單位進行數(shù)據傳輸,它專門用于處理文本文件,底層會涉及字符編碼的轉換。
字節(jié)流的核心類是InputStream和OutputStream,字符流的核心類是Reader和Writer。
兩者都是抽象類,實際開發(fā)中我們使用的是它們的子類,比如FileInputStream、FileWriter等。
? 核心結論:處理非文本文件用字節(jié)流,處理文本文件優(yōu)先用字符流。
1.2.2 代碼實操:字節(jié)流讀寫文本文件
① ?? 創(chuàng)建FileInputStream對象,關聯(lián)要讀取的文本文件test.txt
② ?? 定義byte數(shù)組作為緩沖區(qū),減少IO次數(shù)
③ ?? 讀取數(shù)據并轉換為字符串,輸出到控制臺
④ ?? 關閉流資源,釋放文件句柄
import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamDemo {
public static void main(String[] args) {
FileInputStream fis = null;
try {
// 1. 關聯(lián)文件路徑
fis = new FileInputStream("test.txt");
// 2. 定義緩沖區(qū),大小為1024字節(jié)(1KB)
byte[] buffer = new byte[1024];
int len; // 記錄每次讀取的有效字節(jié)數(shù)
// 3. 循環(huán)讀取數(shù)據
while ((len = fis.read(buffer)) != -1) {
// 將字節(jié)數(shù)組轉換為字符串
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. 關閉流資源
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}?? 注意事項:使用字節(jié)流讀取文本文件時,如果文件編碼是UTF-8,而系統(tǒng)默認編碼是GBK,可能會出現(xiàn)亂碼。這時候需要用字符流或者轉換流來解決。
1.2.3 代碼實操:字符流讀寫文本文件
字符流的優(yōu)勢在于自動處理字符編碼,默認使用系統(tǒng)編碼,也可以手動指定編碼格式。
下面是用FileReader和FileWriter實現(xiàn)文本文件的復制操作:
① ?? 創(chuàng)建FileReader對象讀取源文件,創(chuàng)建FileWriter對象寫入目標文件
② ?? 定義char數(shù)組作為緩沖區(qū),提升讀取效率
③ ?? 循環(huán)讀取源文件數(shù)據,并寫入目標文件
④ ?? 關閉流資源,先關寫入流,再關讀取流
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
// 1. 關聯(lián)源文件和目標文件
fr = new FileReader("source.txt");
fw = new FileWriter("target.txt");
// 2. 定義字符緩沖區(qū)
char[] buffer = new char[1024];
int len;
// 3. 循環(huán)讀寫
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);
// 刷新緩沖區(qū),避免數(shù)據滯留
fw.flush();
}
System.out.println("? 文件復制成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. 關閉流資源,后開先關
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}? 核心結論:字符流讀寫文本文件時,無需手動處理編碼轉換,代碼更簡潔,且不易出現(xiàn)亂碼。
1.2.4 字節(jié)流與字符流的性能對比
?? 沒有緩沖的情況下,字節(jié)流和字符流的讀寫效率相近。
但在處理大文件時,兩者都需要搭配緩沖流來提升性能。
緩沖流的原理是在內存中開辟一塊緩沖區(qū),一次性讀取或寫入大量數(shù)據,減少與磁盤的交互次數(shù)。
字節(jié)緩沖流的類是BufferedInputStream和BufferedOutputStream。
字符緩沖流的類是BufferedReader和BufferedWriter。
下面是緩沖流的性能測試案例:
分別用普通字節(jié)流和緩沖字節(jié)流讀取一個100MB的視頻文件,記錄耗時。
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedStreamTest {
public static void main(String[] args) {
long start = System.currentTimeMillis();
readWithBuffer("large_video.mp4");
long end = System.currentTimeMillis();
System.out.println("緩沖流耗時:" + (end - start) + "ms");
start = System.currentTimeMillis();
readWithoutBuffer("large_video.mp4");
end = System.currentTimeMillis();
System.out.println("普通流耗時:" + (end - start) + "ms");
}
// 使用緩沖字節(jié)流讀取文件
private static void readWithBuffer(String path) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path))) {
byte[] buffer = new byte[1024];
while (bis.read(buffer) != -1) {
// 讀取數(shù)據,不做輸出
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用普通字節(jié)流讀取文件
private static void readWithoutBuffer(String path) {
try (FileInputStream fis = new FileInputStream(path)) {
byte[] buffer = new byte[1024];
while (fis.read(buffer) != -1) {
// 讀取數(shù)據,不做輸出
}
} catch (IOException e) {
e.printStackTrace();
}
}
}測試結果(僅供參考):
緩沖流耗時:120ms 普通流耗時:850ms
? 核心結論:緩沖流能大幅提升IO操作效率,處理大文件時必須使用緩沖流。
1.3 轉換流:解決文件編碼亂碼問題
1.3.1 轉換流的作用
?? 轉換流的作用是字節(jié)流和字符流之間的轉換,它可以指定字符編碼格式,解決文本文件讀寫的亂碼問題。
轉換流的核心類是InputStreamReader和OutputStreamWriter。InputStreamReader:將字節(jié)輸入流轉換為字符輸入流。OutputStreamWriter:將字符輸出流轉換為字節(jié)輸出流。
1.3.2 代碼實操:指定編碼讀取文件
當我們讀取一個UTF-8編碼的文件,而系統(tǒng)默認編碼是GBK時,直接用FileReader會出現(xiàn)亂碼。
此時可以用InputStreamReader指定編碼格式為UTF-8:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class ConvertStreamDemo {
public static void main(String[] args) {
try (InputStreamReader isr = new InputStreamReader(
new FileInputStream("utf8_file.txt"), "UTF-8")) {
char[] buffer = new char[1024];
int len;
while ((len = isr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}?? 注意事項:指定的編碼格式必須和文件的實際編碼一致,否則仍然會出現(xiàn)亂碼。
常見的編碼格式有UTF-8、GBK、GB2312、ISO-8859-1等。
1.4 IO流資源釋放的標準寫法
1.4.1 JDK7之前的寫法:try-catch-finally
在JDK7之前,我們需要在finally塊中手動關閉流資源,并且要判斷流對象是否為null,避免空指針異常。
這種寫法比較繁瑣,但兼容性最好。
1.4.2 JDK7及之后的寫法:try-with-resources
?? JDK7引入了try-with-resources語法,它可以自動關閉實現(xiàn)了AutoCloseable接口的資源,無需手動在finally塊中關閉。
這種寫法更簡潔,代碼可讀性更高,是目前推薦的寫法。
示例代碼:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// 將流對象聲明在try的括號中,自動關閉
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line;
// 按行讀取文本文件
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}? 核心結論:JDK7及以上版本優(yōu)先使用try-with-resources語法,簡化資源釋放代碼。
1.5 實戰(zhàn)案例:文件夾批量復制工具
1.5.1 需求分析
?? 實現(xiàn)一個工具類,能夠復制指定文件夾下的所有文件和子文件夾,包括各種類型的文件(文本、圖片、視頻等)。
要求:
- 支持大文件復制,使用緩沖流提升效率
- 自動創(chuàng)建目標文件夾,避免路徑不存在異常
- 處理復制過程中的IO異常,給出友好提示
1.5.2 代碼實現(xiàn)
import java.io.*;
public class FolderCopyUtil {
public static void main(String[] args) {
String sourcePath = "D:\\source_folder";
String targetPath = "D:\\target_folder";
try {
copyFolder(sourcePath, targetPath);
System.out.println("? 文件夾復制成功!");
} catch (IOException e) {
System.out.println("? 文件夾復制失?。? + e.getMessage());
e.printStackTrace();
}
}
/**
* 復制文件夾
* @param sourcePath 源文件夾路徑
* @param targetPath 目標文件夾路徑
* @throws IOException IO異常
*/
public static void copyFolder(String sourcePath, String targetPath) throws IOException {
File sourceFile = new File(sourcePath);
File targetFile = new File(targetPath);
// 1. 如果源文件不是文件夾,直接復制文件
if (!sourceFile.isDirectory()) {
copyFile(sourceFile, targetFile);
return;
}
// 2. 創(chuàng)建目標文件夾
if (!targetFile.exists()) {
boolean mkdirsSuccess = targetFile.mkdirs();
if (!mkdirsSuccess) {
throw new IOException("創(chuàng)建目標文件夾失?。? + targetPath);
}
}
// 3. 獲取源文件夾下的所有文件和子文件夾
File[] files = sourceFile.listFiles();
if (files == null) {
return;
}
// 4. 循環(huán)復制每個文件和子文件夾
for (File file : files) {
String newSourcePath = file.getAbsolutePath();
String newTargetPath = targetPath + File.separator + file.getName();
copyFolder(newSourcePath, newTargetPath);
}
}
/**
* 復制單個文件
* @param sourceFile 源文件
* @param targetFile 目標文件
* @throws IOException IO異常
*/
public static void copyFile(File sourceFile, File targetFile) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetFile))) {
byte[] buffer = new byte[1024 * 8]; // 8KB緩沖區(qū)
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
bos.flush();
}
}
}
}1.5.3 案例測試與總結
① ?? 創(chuàng)建一個測試文件夾,包含文本、圖片、視頻等多種類型的文件和子文件夾。
② ?? 運行上述代碼,指定源文件夾和目標文件夾路徑。
③ ?? 檢查目標文件夾,確認所有文件和子文件夾都被成功復制。
? 案例總結:這個工具類結合了字節(jié)流、緩沖流的核心知識,是實際開發(fā)中非常實用的功能。
通過這個案例,我們可以掌握IO流的嵌套使用和文件夾遞歸遍歷的技巧。
1.6 本章總結
- 字節(jié)流處理所有類型文件,字符流專門處理文本文件,根據場景選擇合適的流。
- 緩沖流能大幅提升IO效率,處理大文件時必須使用緩沖流。
- 轉換流可以解決文件編碼亂碼問題,通過指定編碼格式實現(xiàn)正確讀寫。
- JDK7及以上版本優(yōu)先使用
try-with-resources語法,自動釋放IO資源。 - 文件夾復制案例綜合運用了IO流的核心知識,是提升實戰(zhàn)能力的重要練習。
到此這篇關于JAVA IO流進階:字符流與字節(jié)流的深度應用的文章就介紹到這了,更多相關java字符流與字節(jié)流內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot整合定時任務之實現(xiàn)Scheduled注解的過程(一個注解全解決)
這篇文章主要介紹了SpringBoot整合定時任務之實現(xiàn)Scheduled注解的過程(一個注解全解決),本文通過使用場景分析給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
簡單快速對@RequestParam聲明的參數(shù)作校驗操作
這篇文章主要介紹了簡單快速對@RequestParam聲明的參數(shù)作校驗操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
springboot快速整合Mybatis組件的方法(推薦)
Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發(fā)過程。這篇文章主要介紹了springboot快速整合Mybatis組件的方法,需要的朋友可以參考下2019-11-11
Spring中@RabbitHandler和@RabbitListener的區(qū)別詳析
@RabbitHandler是用于處理消息的方法注解,它與@RabbitListener注解一起使用,這篇文章主要給大家介紹了關于Spring中@RabbitHandler和@RabbitListener區(qū)別的相關資料,需要的朋友可以參考下2024-02-02

