news 2026/4/17 7:44:03

如何使用 Java IO 进行文件复制?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何使用 Java IO 进行文件复制?

Java IO 文件复制完全指南:从基础到高效(附完整代码实操)

文件复制是 Java IO 最核心的实战场景之一,无论是文本文件、图片、视频还是压缩包,都可以通过 IO 流实现复制。核心原则是:用字节流处理所有文件类型(万能方案),用缓冲流提升效率,用 NIO 简化代码。本文将分 4 种主流实现方式,从基础到进阶,带你彻底掌握文件复制。

一、核心前提(避免踩坑)

  1. 必须用字节流:字符流仅适用于文本文件,复制图片、视频等二进制文件会导致文件损坏,因此所有复制方案均基于字节流;
  2. 缓冲区是效率关键:直接读写单个字节效率极低,用字节数组(缓冲区)批量读写可大幅提升性能;
  3. 资源必须关闭:IO 流是稀缺资源,需用try-with-resources自动关闭(Java 7+ 推荐),避免资源泄露;
  4. 路径处理:需区分相对路径和绝对路径,避免“文件找不到”错误(如test.jpg是项目根目录,src/main/resources/test.jpg是资源目录)。

二、4 种文件复制实现方案(从基础到高效)

方案 1:基础字节流(FileInputStream + FileOutputStream)

最原始的实现方式,直接操作基础字节流,适合理解复制原理(新手入门必学)。

实现代码
importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;/** * 基础字节流文件复制(无缓冲,适合小文件) */publicclassBasicFileCopy{publicstaticvoidmain(String[]args){// 源文件路径(必须存在)、目标文件路径(不存在会自动创建)StringsourcePath="source.jpg";// 可替换为绝对路径:C:/source.jpgStringtargetPath="target_basic.jpg";longstartTime=System.currentTimeMillis();// 计时开始// try-with-resources 自动关闭流,无需手动 close()try(FileInputStreamfis=newFileInputStream(sourcePath);FileOutputStreamfos=newFileOutputStream(targetPath)){intsingleByte;// 存储每次读取的单个字节(0-255)// 循环读取:read() 返回 -1 表示读取完毕while((singleByte=fis.read())!=-1){fos.write(singleByte);// 逐个字节写入}fos.flush();// 强制刷新缓冲区,确保数据写入longcostTime=System.currentTimeMillis()-startTime;System.out.println("基础字节流复制完成!耗时:"+costTime+"ms");}catch(IOExceptione){System.err.println("复制失败:"+e.getMessage());e.printStackTrace();}}}
代码说明
  • 核心逻辑:fis.read()逐个读取字节,fos.write()逐个写入,本质是“字节级拷贝”;
  • 优点:代码简单,易理解,无需额外包装;
  • 缺点:效率极低(频繁磁盘 IO),仅适合100KB 以下的小文件,大文件(如 100MB 视频)会卡顿。

方案 2:基础字节流 + 缓冲区(推荐入门使用)

在方案 1 基础上添加字节数组缓冲区,批量读写字节,效率比方案 1 提升 10-100 倍,是日常开发的“基础优选方案”。

实现代码
importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;/** * 基础字节流 + 缓冲区(适合中、小文件,效率较高) */publicclassBufferedArrayFileCopy{publicstaticvoidmain(String[]args){StringsourcePath="source.mp4";// 测试大文件(如 100MB 视频)StringtargetPath="target_buffered_array.mp4";longstartTime=System.currentTimeMillis();try(FileInputStreamfis=newFileInputStream(sourcePath);FileOutputStreamfos=newFileOutputStream(targetPath)){// 缓冲区:4KB(常用最优大小,可调整为 8192、16384 等 2^n 数值)byte[]buffer=newbyte[4096];intreadLen;// 记录每次实际读取的字节数(最多为缓冲区大小)// 批量读取:read(buffer) 填充缓冲区,返回实际读取长度while((readLen=fis.read(buffer))!=-1){// 写入实际读取的字节(避免写入缓冲区中未使用的部分)fos.write(buffer,0,readLen);}fos.flush();longcostTime=System.currentTimeMillis()-startTime;System.out.println("缓冲区字节流复制完成!耗时:"+costTime+"ms");}catch(IOExceptione){System.err.println("复制失败:"+e.getMessage());e.printStackTrace();}}}
代码说明
  • 核心优化:byte[] buffer = new byte[4096]一次性读取 4KB 数据,减少磁盘 IO 次数(IO 是磁盘操作,耗时远高于内存操作);
  • readLen关键作用:最后一次读取可能未满缓冲区,write(buffer, 0, readLen)仅写入实际读取的字节,避免文件末尾出现垃圾数据;
  • 优点:效率高,代码简洁,无额外依赖,适合10MB-1GB 的中大型文件
  • 注意:缓冲区大小并非越大越好(如 1GB 缓冲区会占用过多内存),4KB/8KB/16KB 是平衡内存和效率的最优选择。

方案 3:缓冲流包装(BufferedInputStream + BufferedOutputStream)

Java 提供的专用缓冲流,内部自带缓冲区(默认 8KB),无需手动定义字节数组,是“高效简洁方案”。

实现代码
importjava.io.BufferedInputStream;importjava.io.BufferedOutputStream;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;/** * 缓冲流包装(推荐生产环境使用,高效且简洁) */publicclassBufferedStreamFileCopy{publicstaticvoidmain(String[]args){StringsourcePath="source.zip";// 压缩包等二进制文件StringtargetPath="target_buffered_stream.zip";longstartTime=System.currentTimeMillis();// 缓冲流包装基础字节流:BufferedInputStream 自带 8KB 缓冲区try(BufferedInputStreambis=newBufferedInputStream(newFileInputStream(sourcePath));BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream(targetPath))){byte[]buffer=newbyte[4096];// 可手动指定缓冲区大小(建议与内部缓冲区匹配)intreadLen;while((readLen=bis.read(buffer))!=-1){bos.write(buffer,0,readLen);}bos.flush();// 缓冲流需手动刷新,确保数据写入longcostTime=System.currentTimeMillis()-startTime;System.out.println("缓冲流复制完成!耗时:"+costTime+"ms");}catch(IOExceptione){System.err.println("复制失败:"+e.getMessage());e.printStackTrace();}}}
代码说明
  • 设计思想:装饰器模式(缓冲流包装基础流,增强缓冲功能);
  • 优势:
    1. 内部优化:BufferedInputStream会预读取数据到缓冲区,减少磁盘 IO;
    2. 代码简洁:无需关心缓冲区底层实现,仅需调用基础读写方法;
    3. 效率最优:比方案 2 略快(内部有额外优化),是生产环境首选方案
  • 注意:缓冲流必须调用flush()或等待缓冲区满,数据才会写入目标文件,try-with-resources关闭流时会自动刷新,但建议手动调用更稳妥。

方案 4:Java 8+ NIO.2(Files 工具类)

Java 7 引入的 NIO.2 提供了Files工具类,一行代码即可实现文件复制,底层优化充分,适合简洁场景。

实现代码
importjava.nio.file.Files;importjava.nio.file.Paths;importjava.nio.file.StandardCopyOption;importjava.io.IOException;/** * NIO.2 Files 工具类(Java 8+ 推荐,一行代码复制) */publicclassNioFilesCopy{publicstaticvoidmain(String[]args){StringsourcePath="source.pdf";StringtargetPath="target_nio.pdf";longstartTime=System.currentTimeMillis();try{// 核心方法:Files.copy(源路径, 目标路径, 复制选项)Files.copy(Paths.get(sourcePath),// 源文件路径(Path 对象)Paths.get(targetPath),// 目标文件路径StandardCopyOption.REPLACE_EXISTING// 选项:目标文件存在则覆盖);longcostTime=System.currentTimeMillis()-startTime;System.out.println("NIO Files 复制完成!耗时:"+costTime+"ms");}catch(IOExceptione){System.err.println("复制失败:"+e.getMessage());e.printStackTrace();}}}
代码说明
  • 核心优点:
    1. 代码极简:一行代码完成复制,无需手动处理流;
    2. 底层高效:内部使用 NIO 通道(Channel)和缓冲区,性能不逊于缓冲流;
    3. 功能强大:支持多种复制选项(如覆盖、原子操作等);
  • 常用复制选项:
    • StandardCopyOption.REPLACE_EXISTING:目标文件存在则覆盖(默认不覆盖,会抛异常);
    • StandardCopyOption.COPY_ATTRIBUTES:复制文件属性(如创建时间、权限);
    • StandardCopyOption.ATOMIC_MOVE:原子操作(仅适用于同一文件系统);
  • 适用场景:快速实现复制,无需自定义缓冲区或流,适合日常开发、脚本工具等场景。

三、常见问题与避坑指南

1. 文件找不到异常(FileNotFoundException)

  • 原因:源文件路径错误,或源文件不存在;
  • 解决方案:
    • 用绝对路径测试(如C:/Users/xxx/source.jpg);
    • 读取资源目录文件(如src/main/resources)时,用类加载器获取路径:
      // 获取 resources 下的文件路径StringsourcePath=NioFilesCopy.class.getClassLoader().getResource("source.jpg").getPath();

2. 目标文件被占用(IOException: 另一个程序正在使用此文件)

  • 原因:目标文件已被其他程序打开(如图片被图片查看器占用);
  • 解决方案:关闭占用目标文件的程序,或更换目标文件名称。

3. 复制大文件内存溢出(OutOfMemoryError)

  • 原因:方案 1 中逐个字节读取效率低,或缓冲区设置过大(如 1GB);
  • 解决方案:使用方案 2/3/4,缓冲区设置为 4KB-16KB,避免一次性加载大量数据到内存。

4. 复制后文件损坏

  • 原因:误用字符流复制二进制文件(如FileReader/FileWriter);
  • 解决方案:所有文件复制均使用字节流(InputStream/OutputStream)或 NIOFiles工具类。

四、4 种方案对比与选择建议

方案核心类优点缺点适用场景
基础字节流FileInputStream + FileOutputStream代码简单,易理解效率极低新手学习,小文件(<100KB)
基础字节流+缓冲区FileInputStream + FileOutputStream + 字节数组效率高,无额外依赖需手动管理缓冲区中大型文件(10MB-1GB),需自定义缓冲区
缓冲流包装BufferedInputStream + BufferedOutputStream效率最优,代码简洁需手动刷新生产环境首选,各类文件(推荐)
NIO.2 FilesFiles.copy()一行代码,底层优化灵活性低(难自定义)日常开发、脚本工具,无需自定义逻辑

最终选择建议:

  • 学习阶段:先掌握方案 1(理解原理)→ 方案 2(掌握缓冲区);
  • 开发阶段:优先使用方案 3(缓冲流)或方案 4(NIO Files),兼顾效率和简洁性;
  • 大文件(>1GB):方案 3(缓冲流)或方案 4(NIO),避免内存溢出。

总结

Java 文件复制的核心是“字节流 + 缓冲区”,无论哪种方案,本质都是“读取源文件字节 → 写入目标文件字节”。新手建议从基础方案入手,理解流的读写逻辑和缓冲区原理;实际开发中优先使用缓冲流或 NIO Files 工具类,兼顾效率和代码简洁性。记住:复制时始终用字节流,关闭资源用try-with-resources,缓冲区设置为 4KB-16KB,即可避免绝大多数问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 23:00:02

新能源工程机械远程智慧运维管理系统

一、行业背景在“双碳”战略深入推进与新型基础设施建设加速的双重驱动下&#xff0c;新能源工程机械行业正迎来爆发式增长。传统燃油工程机械因高排放、高能耗逐渐被市场淘汰&#xff0c;电动挖掘机、新能源起重机、纯电动装载机等产品已成为工程建设领域的主流选择。然而&…

作者头像 李华
网站建设 2026/4/17 13:11:46

基于强化学习的神经网络优化算法(RLNNA)优化机器人路径规划

基于强化学习的神经网络优化算法&#xff08;RLNNA&#xff09;优化机器人路径规划 基于强化学习的神经网络优化算法&#xff08;RLNNA&#xff09;是一种利用强化学习来优化神经网络的算法。 它通过强化学习来训练神经网络&#xff0c;使其能够更好地适应不同的任务和环境。 在…

作者头像 李华
网站建设 2026/3/23 1:16:32

PCIe调试终极指南:aer_inject工具完整实战教程

PCIe调试终极指南&#xff1a;aer_inject工具完整实战教程 【免费下载链接】linux Linux kernel source tree 项目地址: https://gitcode.com/GitHub_Trending/li/linux 开篇&#xff1a;为什么你的PCIe设备总是神秘崩溃&#xff1f; 作为一名Linux系统管理员&#xff…

作者头像 李华
网站建设 2026/4/13 16:45:30

20251213_WPF入门ICommand命令_Study08

一、 ICommand接口 ICommand接口定义在System.Windows.Input命名空间中&#xff0c;包含三个主要成员&#xff1a; Execute: 当命令被触发时执行的方法&#xff0c;通常包含业务逻辑。CanExecute: 判断命令是否可执行的方法&#xff0c;返回布尔值&#xff08;如基于某些条件禁…

作者头像 李华
网站建设 2026/4/16 14:03:39

Hermes JavaScript引擎终极完整指南:从快速上手到深度优化

Hermes JavaScript引擎终极完整指南&#xff1a;从快速上手到深度优化 【免费下载链接】hermes A JavaScript engine optimized for running React Native. 项目地址: https://gitcode.com/gh_mirrors/hermes/hermes Hermes是Facebook专为React Native优化的JavaScript引…

作者头像 李华