在 Java 编程中,文件复制是非常常见的 IO 操作,而根据文件类型的不同,我们通常会选择不同的流来实现。文本文件复制我们最常用字符缓冲流,而所有类型文件的 “万能复制” 则依赖字节缓冲流。今天就来把这两种场景讲清楚,附完整可运行代码和原理分析~


一、文本文件复制:字符缓冲流(最常用方案)

1. 为什么文本文件优先用字符流?

字符流(Reader/Writer)是 Java 专为处理文本数据设计的,它会自动处理字符编码,避免乱码问题。而BufferedReaderBufferedWriter作为字符缓冲流,自带缓冲区,大幅提升读写效率,是文本文件复制的首选方案。

2. 核心 API 说明

  • BufferedReader(Reader in):创建字符输入缓冲流,常用构造器传入FileReader
  • String readLine():按行读取文本,是字符流最方便的读取方式
  • BufferedWriter(Writer out):创建字符输出缓冲流,常用构造器传入FileWriter
  • newLine():写入跨平台换行符,避免不同系统的换行差异问题

3. 完整实现代码

java

运行

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class TextFileCopyDemo {
    public static void main(String[] args) {
        // 定义源文件路径和目标文件路径
        String sourcePath = "source.txt";
        String targetPath = "target.txt";

        // 声明流对象,用try-with-resources自动关闭资源
        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();
        }
    }
}

4. 关键细节说明

  • try-with-resources:Java 7 + 提供的语法,流对象会自动调用close()方法,避免资源泄漏,比手动finally关闭更简洁安全。
  • readLine() + newLine():按行读写不仅效率高,还能完美保留文本的换行格式,比单个字符读取更适合文本场景。
  • 编码问题FileReader/FileWriter默认使用平台编码(Windows 为 GBK,Linux/macOS 为 UTF-8),如果需要指定编码,可以使用InputStreamReader/OutputStreamWriter作为桥梁,例如:

    java

    运行

    new BufferedReader(new InputStreamReader(new FileInputStream(sourcePath), "UTF-8"));
    

二、任意文件复制:字节缓冲流(万能复制方案)

1. 为什么字节流是 “万能” 的?

字节流(InputStream/OutputStream)以字节为单位读写数据,不管是文本文件、图片、视频、压缩包还是二进制文件,本质上都是字节序列,因此字节流可以复制所有类型的文件。而BufferedInputStreamBufferedOutputStream通过内置缓冲区减少 IO 次数,大幅提升大文件的复制效率。

2. 核心 API 说明

  • BufferedInputStream(InputStream in):创建字节输入缓冲流,传入FileInputStream
  • int read(byte[] b):批量读取字节到数组,返回读取到的字节数,-1 表示读取完毕
  • BufferedOutputStream(OutputStream out):创建字节输出缓冲流,传入FileOutputStream
  • write(byte[] b, int off, int len):写入数组中从 off 开始、长度为 len 的字节,避免写入无效数据

3. 完整实现代码

java

运行

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class AnyFileCopyDemo {
    public static void main(String[] args) {
        // 支持任意文件:图片、视频、压缩包、文本文件都可以
        String sourcePath = "source.jpg";
        String targetPath = "target.jpg";

        // 声明缓冲流对象,自动关闭资源
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) {

            // 定义缓冲区数组,大小通常为1024的倍数,常用8KB(8192字节)
            byte[] buffer = new byte[8192];
            int len;
            // 批量读取数据到缓冲区
            while ((len = bis.read(buffer)) != -1) {
                // 写入实际读取到的字节,len为本次读取的有效字节数
                bos.write(buffer, 0, len);
            }
            System.out.println("文件复制完成!");
        } catch (IOException e) {
            System.err.println("文件复制失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

4. 关键细节说明

  • 缓冲区大小选择:数组大小一般设为1024的倍数,比如4096(4KB)、8192(8KB),太大可能造成内存浪费,太小会增加 IO 次数,8KB 是比较通用的选择。
  • 为什么用write(buffer, 0, len)read(buffer)可能不会把数组填满(比如文件最后一段数据),如果直接write(buffer)会把数组中无效的字节也写入文件,导致文件损坏,因此必须写入len个有效字节。
  • 适用场景:除了文本文件,更适合图片、视频、音频、压缩包等二进制文件,是 Java 中文件复制的通用方案。

三、两种方案对比与选型建议

表格

特性 字符缓冲流(BufferedReader/BufferedWriter) 字节缓冲流(BufferedInputStream/BufferedOutputStream)
适用文件类型 纯文本文件 所有文件(文本、图片、视频、二进制文件)
处理单位 字符(char) 字节(byte)
编码处理 自动处理字符编码,避免乱码 不处理编码,按原始字节复制,文本文件可能出现乱码
常用读写方式 按行读写(readLine ()) 按字节数组批量读写
效率对比 文本文件场景下高效,且操作方便 所有场景通用,大文件复制效率高
推荐场景 纯文本文件复制、文本文件读写处理 任意文件复制、二进制文件读写

四、避坑指南与常见问题

  1. 文本文件用字节流复制会乱码吗? 不会损坏文件,但如果中途修改或查看文本内容,字节流无法处理编码,直接读取的字节转成字符串可能出现乱码。如果只是复制文件本身,字节流复制文本文件是完全没问题的,只是不适合边读边处理文本内容。

  2. 流对象不关闭会有什么问题? 数据可能还在缓冲区中,没有真正写入文件,导致目标文件不完整;同时会占用文件句柄,造成资源泄漏。使用try-with-resources语法可以彻底避免这个问题。

  3. 大文件复制会导致内存溢出吗? 只要不把整个文件一次性读入内存,就不会溢出。上面的批量读写方案每次只读取 8KB 数据,内存占用极低,哪怕复制几个 GB 的文件也完全没问题。


五、总结

  • 字符缓冲流:是文本文件复制的最优选择,按行读写方便、效率高,还能自动处理字符编码,适合纯文本场景。
  • 字节缓冲流:是文件复制的 “万能钥匙”,不管什么类型的文件都能复制,批量读写的方式也保证了效率,适合所有通用场景。
  • 核心要点:缓冲流通过减少 IO 次数提升效率,try-with-resources自动关闭资源,批量读写时注意写入有效数据长度。

更多推荐