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

