Java 文件复制:字符缓冲流与字节缓冲流
文件复制是 Java IO 编程中最基础、最核心的操作之一,也是编程竞赛、课程作业和实际开发中高频出现的需求。很多初学者在面对文件复制时,往往会混淆字符流和字节流的使用场景,写出效率低下甚至会损坏文件的代码。
本文将通过实战代码,详细讲解两种最常用的文件复制方案:
字符缓冲流(BufferedReader/BufferedWriter):专门用于高效复制文本文件
字节缓冲流(BufferedInputStream/BufferedOutputStream):万能复制方案,支持所有类型文件
读完本文,你将彻底掌握 Java 文件复制的正确姿势,写出高效、健壮、无 bug 的复制代码。
一、文本文件复制:字符缓冲流(最常用)
1.1 为什么用字符缓冲流处理文本?
字符流专门用于处理字符数据,会自动处理字符编码(如 UTF-8、GBK),避免出现乱码问题。而缓冲流在普通流的基础上增加了内存缓冲区,可以一次性读取 / 写入多个字符,大幅减少磁盘 IO 次数,将文件复制效率提升数倍。
核心类:
BufferedReader:字符缓冲输入流,提供readLine()方法可以按行读取文本,非常方便
BufferedWriter:字符缓冲输出流,提供newLine()方法可以跨平台写入换行符
1.2 完整可运行代码
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
-
文本文件复制工具类(字符缓冲流实现)
-
适用场景:.txt、.java、.md、.html等所有纯文本文件
*/
public class TextFileCopy {
public static void main(String[] args) {
// 源文件路径和目标文件路径
String sourcePath = “source.txt”;
String targetPath = “target.txt”;// 使用try-with-resources语法自动关闭流,无需手动finally try (BufferedReader br = new BufferedReader(new FileReader(sourcePath)); BufferedWriter bw = new BufferedWriter(new FileWriter(targetPath))) { String line; // 按行读取,直到返回null表示文件结束 while ((line = br.readLine()) != null) { bw.write(line); // 写入一行内容 bw.newLine(); // 写入换行符,跨平台兼容 } System.out.println("文本文件复制成功!"); } catch (IOException e) { System.err.println("文件复制失败:" + e.getMessage()); e.printStackTrace(); }}
}
1.3 代码详解与注意事项
自动资源管理:使用 Java 7 引入的try-with-resources语法,流对象会在代码块结束后自动关闭,彻底避免资源泄漏
按行读取:readLine()方法读取一行文本,不包含末尾的换行符,因此需要手动调用newLine()写入换行
编码问题:FileReader和FileWriter默认使用系统平台编码。如果需要指定编码(如 UTF-8),请使用InputStreamReader和OutputStreamWriter包装:
// 指定UTF-8编码的写法
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(sourcePath), “UTF-8”));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetPath), “UTF-8”));
⚠️ 重要限制:字符缓冲流只能用于纯文本文件,绝对不能用于复制图片、视频、音频、exe 等二进制文件,否则会导致文件损坏无法打开。
二、任意文件复制:字节缓冲流(万能复制)
2.1 为什么字节缓冲流是万能的?
字节流直接操作文件的二进制字节数据,不关心文件的类型和编码,因此可以复制任何类型的文件(文本、图片、视频、压缩包等)。同样,字节缓冲流通过缓冲区减少磁盘 IO 次数,效率远高于普通字节流。
核心类:
BufferedInputStream:字节缓冲输入流
BufferedOutputStream:字节缓冲输出流
2.2 完整可运行代码
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
-
任意文件复制工具类(字节缓冲流实现)
-
适用场景:所有类型文件,包括二进制文件和文本文件
*/
public class AnyFileCopy {
public static void main(String[] args) {
// 示例:复制一张图片,也可以换成视频、压缩包等任何文件
String sourcePath = “image.jpg”;
String targetPath = “image_copy.jpg”;try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath)); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) { // 定义字节数组作为缓冲区,大小通常设为8KB(8192字节),性能最优 byte[] buffer = new byte[8192]; int len; // 读取缓冲区大小的字节,返回实际读取的字节数,返回-1表示文件结束 while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); // 只写入实际读取的字节数,避免写入多余数据 } // 刷新缓冲区,确保所有数据都写入磁盘 bos.flush(); System.out.println("任意文件复制成功!"); } catch (IOException e) { System.err.println("文件复制失败:" + e.getMessage()); e.printStackTrace(); }}
}
2.3 代码详解与优化技巧
缓冲区大小:默认缓冲区大小是 8KB(8192 字节),这是经过大量测试得出的最优值。不要设置过小(如 1 字节),否则会频繁 IO;也不要设置过大(如 1MB),否则会浪费内存。
写入方式:必须使用write(buffer, 0, len),而不是write(buffer)。因为最后一次读取可能没有填满缓冲区,只写入实际读取的len个字节,避免目标文件末尾出现多余的空字节。
flush () 方法:虽然缓冲流在关闭时会自动刷新,但显式调用flush()可以确保数据立即写入磁盘,防止数据丢失。
通用性:这个代码可以复制任何文件,包括文本文件。如果不确定文件类型,直接使用字节缓冲流是最安全的选择。
三、两种复制方式对比与选型

选型建议:
明确知道是文本文件时,优先使用字符缓冲流,代码更简洁,处理文本更方便
不确定文件类型或需要复制二进制文件时,必须使用字节缓冲流
编程竞赛中,如果题目没有特别说明,直接写字节缓冲流的万能复制代码,不会出错
四、常见坑点与避坑指南
忘记关闭流:这是最常见的错误,会导致资源泄漏,文件句柄无法释放。一定要使用try-with-resources语法自动关闭流。
字符流复制二进制文件:会导致文件损坏,切记字符流只能处理文本。
缓冲区大小设置不当:不要使用 1 字节的缓冲区,效率极低。推荐使用 8KB 的默认值。
使用 write (buffer) 而不是 write (buffer, 0, len):会导致目标文件末尾出现多余的空字节,文件大小变大。
路径问题:相对路径是相对于项目根目录,绝对路径要注意 Windows 系统的反斜杠需要转义(C:\file.txt),或者使用正斜杠(C:/file.txt)。
五、总结
本文详细讲解了 Java 中两种最常用的文件复制方式:
字符缓冲流是文本文件复制的首选,效率高、代码简洁,支持按行处理
字节缓冲流是万能复制方案,支持所有类型文件,通用性最强
掌握这两种方法,你就能轻松应对所有文件复制需求。在实际开发中,根据文件类型选择合适的流,同时注意避免常见的坑点,就能写出高效、健壮的文件复制代码。
更多推荐
所有评论(0)