Java IO流进阶:字节流与字符流的深度应用
Java IO流进阶:字节流与字符流的深度应用 !IO流架构示意图 1.1 本章学习目标与重点 掌握字节流与字符流的核心区别,能够根据实际开发场景选择合适的 IO 流实现文件操作。 熟练运用缓冲流提升 IO 操作效率,解决大文件读写的性能问题。 理解转换流的作用,处理不同编码格式的文件读写,避免乱码问题。 **重点提示**:本章核心在于**流的嵌套使用**和**资源释放的标准写法**,这是实际开发…

Java IO流进阶:字节流与字符流的深度应用 !IO流架构示意图 1.1 本章学习目标与重点 掌握字节流与字符流的核心区别,能够根据实际开发场景选择合适的 IO 流实现文件操作。 熟练运用缓冲流提升 IO 操作效率,解决大文件读写的性能问题。 理解转换流的作用,处理不同编码格式的文件读写,避免乱码问题。 **重点提示**:本章核心在于**流的嵌套使用**和**资源释放的标准写法**,这是实际开发…

byte 为基本单位进行数据传输,可处理所有类型的文件(如图片、视频、音频、文本等)。char 为基本单位进行数据传输,专门用于处理文本文件,底层会自动涉及字符编码的转换。字节流的核心抽象类是 InputStream 和 OutputStream,字符流的核心抽象类是 Reader 和 Writer。实际开发中通常使用其子类,如 FileInputStream、FileWriter 等。
核心结论:处理非文本文件必须使用字节流;处理纯文本文件优先使用字符流。
FileInputStream 对象,关联目标文本文件 test.txt。byte 数组作为缓冲区,减少磁盘 IO 次数。finally 块中安全关闭流资源,释放文件句柄。import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamDemo {
public static void main(String[] args) {
FileInputStream fis = null;
try {
// 1. 关联文件路径
fis = new FileInputStream("test.txt");
// 2. 定义缓冲区,大小为 1024 字节(1KB)
byte[] buffer = new byte[1024];
int len; // 记录每次读取的有效字节数
// 3. 循环读取数据
while ((len = fis.read(buffer)) != -1) {
// 将字节数组转换为字符串
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();
}
}
}
}
}
注意事项:使用字节流读取文本文件时,若文件编码为 UTF-8 而系统默认编码为 GBK,直接转换字符串可能导致乱码。此时应使用字符流或转换流。
字符流的优势在于自动处理字符编码(默认使用系统编码,也可手动指定)。以下使用 FileReader 和 FileWriter 实现文本文件复制:
FileReader 读取源文件,创建 FileWriter 写入目标文件。char 数组作为缓冲区,提升读取效率。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. 关联源文件和目标文件
fr = new FileReader("source.txt");
fw = new FileWriter("target.txt");
// 2. 定义字符缓冲区
char[] buffer = new char[1024];
int len;
// 3. 循环读写
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);
// 刷新缓冲区,避免数据滞留
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(); }
}
}
}
}
核心结论:字符流读写文本文件时无需手动处理编码转换,代码更简洁且不易出现乱码。
在无缓冲的情况下,字节流与字符流的读写效率相近。但在处理大文件时,两者均需搭配缓冲流以提升性能。
缓冲流通过在内存中开辟缓冲区,批量读写数据,从而大幅减少与磁盘的交互次数。
BufferedInputStream / BufferedOutputStreamBufferedReader / BufferedWriter以下为缓冲流性能测试示例(读取 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");
}
// 使用缓冲字节流读取文件
private static void readWithBuffer(String path) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path))) {
byte[] buffer = new byte[1024];
while (bis.read(buffer) != -1) {
// 仅测试读取性能,不做输出
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用普通字节流读取文件
private static void readWithoutBuffer(String path) {
try (FileInputStream fis = new FileInputStream(path)) {
byte[] buffer = new byte[1024];
while (fis.read(buffer) != -1) {
// 仅测试读取性能,不做输出
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试结果参考:
缓冲流耗时:120ms
普通流耗时:850ms
核心结论:缓冲流能大幅提升 IO 操作效率,处理大文件时强烈建议使用缓冲流。
转换流用于实现字节流与字符流之间的桥接,允许在转换过程中显式指定字符编码,从而彻底解决文本文件读写的乱码问题。
InputStreamReader:将字节输入流转换为字符输入流。OutputStreamWriter:将字符输出流转换为字节输出流。当读取 UTF-8 编码文件而系统默认编码为 GBK 时,直接使用 FileReader 会导致乱码。此时可通过 InputStreamReader 显式指定编码:
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();
}
}
}
注意事项:指定的编码格式必须与文件实际编码严格一致,否则仍会乱码。常见编码包括 UTF-8、GBK、GB2312、ISO-8859-1 等。
在 JDK 7 之前,必须在 finally 块中手动关闭流资源,并严格判空以防止 NullPointerException。该写法较为繁琐,但兼容性最佳。
JDK 7 引入了 try-with-resources 语法,可自动关闭所有实现 AutoCloseable 接口的资源,彻底免除了手动编写 finally 的繁琐。该写法简洁且可读性高,为当前官方推荐标准。
示例代码:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// 将流对象声明在 try 的括号中,JVM 会自动调用 close()
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();
}
}
}
核心结论:JDK 7 及以上项目应优先使用
try-with-resources语法管理 IO 资源。
实现一个工具类,支持递归复制指定目录下的所有文件与子目录(涵盖文本、图片、视频等任意类型)。 核心要求:
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 目标文件夹路径
*/
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. 创建目标文件夹
if (!targetFile.exists()) {
if (!targetFile.mkdirs()) {
throw new IOException("创建目标文件夹失败:" + targetPath);
}
}
// 3. 遍历源目录下的所有文件与子目录
File[] files = sourceFile.listFiles();
if (files == null) return;
for (File file : files) {
String newSourcePath = file.getAbsolutePath();
String newTargetPath = targetPath + File.separator + file.getName();
copyFolder(newSourcePath, newTargetPath);
}
}
/**
* 使用缓冲流复制单个文件
*/
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[8192]; // 8KB 缓冲区
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
}
}
}
案例总结:该工具类综合运用了字节流、缓冲流与递归遍历逻辑,是 IO 流嵌套使用的典型实践。掌握此模式可有效应对日常开发中的文件批量处理需求。
try-with-resources 语法,保障资源安全释放。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online