“复制文件看似简单,却是展示代码功底的好机会!” 🎯 无论是期末作业、毕业设计,还是项目开发,本文带你全面掌握 Java 文件复制的 4 种方法:经典的流(Stream)、高效的 FileChannel、便捷的 Apache Commons IO,以及现代化的 Files 类,外加 1GB 文件性能对比,帮你选出最快、最适合的方案!💡 阅读后别忘了 点赞 👍、收藏 ⭐、关注 👀、分享 🔗,一起提升 Java 技能,让你的代码更出彩、更高效!✨。如果你有更好的方法,欢迎在评论区留言,有任何疑问也可以私信作者,给你解答。
Java 文件复制是一个非常常见的操作。但是 java.io.File
类并没有提供从源文件复制到目标文件的快捷方法。本文中介绍在 Java 中实现文件复制的四种不同方式。
一.使用流(Stream)复制文件
这是在 Java 中实现文件复制的传统方式。在这种方法中,我们创建两个文件对象——源文件(source)和目标文件(destination)。然后,我们从源文件创建输入流(InputStream),并通过输出流(OutputStream)将数据写入目标文件。
以下是使用流(Stream)实现文件复制的方法代码
// 使用流(Stream)复制文件的方法
private static void copyFileUsingStream(File source, File dest) throws IOException {
InputStream is = null; // 定义输入流对象,用于从源文件中读取数据
OutputStream os = null; // 定义输出流对象,用于将数据写入目标文件
try {
// 创建输入流,指向源文件
is = new FileInputStream(source);
// 创建输出流,指向目标文件
os = new FileOutputStream(dest);
// 定义一个字节缓冲区,用于临时存储读取的数据,大小为 1024 字节
byte[] buffer = new byte[1024];
int length; // 定义变量,用于存储每次读取的字节数
// 循环读取源文件的数据,直到文件结束(read() 返回 -1)
while ((length = is.read(buffer)) > 0) {
// 将读取的数据写入目标文件,从缓冲区的起始位置开始写入,写入长度为读取的字节数
os.write(buffer, 0, length);
}
} finally {
// 关闭输入流,释放资源
if (is != null) {
is.close();
}
// 关闭输出流,释放资源
if (os != null) {
os.close();
}
}
}
1.1.方法说明
- 输入流和输出流:通过
FileInputStream
和FileOutputStream
创建输入和输出流,用于读取源文件和写入目标文件。- 缓冲区:
byte[] buffer
用于临时存储从源文件中读取的数据,避免频繁操作文件,提高效率。- 循环读取与写入:通过
is.read(buffer)
方法从源文件中读取数据,并通过os.write(buffer, 0, length)
将数据写入目标文件。- 关闭资源:在
finally
块中关闭流,以确保无论是否发生异常,资源都会被正确释放。1.2.注意事项
- 异常处理:代码中使用了
try-finally
块来确保资源的正确释放。- 缓冲区大小:缓冲区大小可以根据文件大小和性能需求进行调整,以提高效率。
二.使用java.nio.channels.FileChannel
实现文件复制
Java NIO 类是在 Java 1.4 中引入的,其中 FileChannel
可用于在 Java 中复制文件。根据 javadoc中transferFrom()
方法的介绍,这种文件复制方法的速度通常比使用流(Streams)更快。
以下是使用 FileChannel
复制文件的方法实现。
// 使用 FileChannel 实现文件复制
private static void copyFileUsingChannel(File source, File dest) throws IOException {
FileChannel sourceChannel = null; // 定义源文件的 FileChannel
FileChannel destChannel = null; // 定义目标文件的 FileChannel
try {
// 创建源文件的 FileChannel,使用 FileInputStream 打开源文件
sourceChannel = new FileInputStream(source).getChannel();
// 创建目标文件的 FileChannel,使用 FileOutputStream 打开目标文件
destChannel = new FileOutputStream(dest).getChannel();
// 使用 FileChannel 的 transferFrom 方法从源文件复制内容到目标文件
// 参数 0 表示从文件的开头开始复制
// sourceChannel.size() 获取源文件的总大小(字节数)
destChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
} finally {
// 关闭源文件的 FileChannel,释放资源
if (sourceChannel != null) sourceChannel.close();
// 关闭目标文件的 FileChannel,释放资源
if (destChannel != null) destChannel.close();
}
}
2.1.方法说明:
FileChannel
:
- 是
java.nio
包中用于文件读写的通道类。- 它支持文件的高效读写,尤其适合处理大文件。
transferFrom()
方法:
- 这是
FileChannel
提供的一个方法,用于从一个通道(sourceChannel)复制内容到当前通道(destChannel)。- 参数说明:
- 第一个参数:源文件的通道对象。
- 第二个参数:目标文件中写入数据的起始位置。
- 第三个参数:需要复制的字节数。
代码关键点:
- 在
try
块中初始化资源,并在finally
块中确保释放资源(关闭通道)。- 使用
FileInputStream
和FileOutputStream
创建文件输入流和输出流,并从中获取FileChannel
。
通过这种方式复制文件,效率较高,适合处理大文件,但具体性能提升与文件大小和硬件配置有关。
三.使用 Apache Commons IO 的 FileUtils 进行文件复制
Apache Commons IO
提供了一个简单的工具方法 FileUtils.copyFile(File srcFile, File destFile)
来实现文件复制。如果你的项目中已经引入了 Apache Commons IO
库,使用它可以使代码更简单明了。
注意:
FileUtils.copyFile
方法内部使用了 Java NIO 的FileChannel
,因此如果你的项目中没有其他功能依赖 Apache Commons IO,可以直接使用FileChannel
而没有必要单独引入Apache Commons IO库
以下是使用 Apache Commons IO 进行文件复制的代码示例:
// 使用 Apache Commons IO 的 FileUtils 方法实现文件复制
private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
// 使用 FileUtils.copyFile 方法将源文件复制到目标文件
FileUtils.copyFile(source, dest);
}
3.1.方法说明:
File source
:表示源文件对象。File dest
:表示目标文件对象。FileUtils.copyFile()
:Apache Commons IO 提供的静态方法,用于文件复制操作。
优点:
- 代码简单,易于理解。
- 如果项目已经依赖 Apache Commons IO,使用该方法是一个方便的选择。
- 内部基于 Java NIO 的
FileChannel
,具有较高的性能。缺点:
- 如果项目中没有其他功能需要 Apache Commons IO,仅为了文件复制而引入这个库可能有些多余。
此方法适用于希望通过简单方式实现文件复制且项目已使用 Apache Commons IO 的场景。
四.使用 Files
类实现文件复制
如果你使用的是 Java 7 或更高版本,可以使用 java.nio.file.Files
类的 copy()
方法来复制文件。该方法通过文件系统提供器(File System Providers)执行文件复制操作,非常方便和简洁。
以下是使用 Files
类的 copy()
方法 进行文件复制的代码示例
// 使用 Java 7 的 Files.copy 方法实现文件复制
private static void copyFileUsingJava7Files(File source, File dest) throws IOException {
// 使用 Files.copy 方法从源文件的路径 (source.toPath()) 复制数据
// 到目标文件的路径 (dest.toPath())
Files.copy(source.toPath(), dest.toPath());
}
4.1.方法说明
source.toPath()
:
- 将源文件
File
对象转换为路径对象(Path
)。- 这是
Files.copy()
方法的输入参数之一。
dest.toPath()
:
- 将目标文件
File
对象转换为路径对象(Path
)。- 用于指定复制操作的目标位置。
异常处理:
- 方法声明抛出
IOException
,表明在复制过程中可能会发生 I/O 异常,例如文件不存在或文件不可读写。优点
- 简洁性:
Files.copy()
是一个一行代码的实现,适合需要快速实现文件复制的场景。- 兼容性:适用于 Java 7 及以上版本。
- 灵活性:支持文件系统的抽象,可以与多种文件系统提供器(例如本地文件系统、虚拟文件系统)一起使用。
如果需要进一步扩展,还可以结合
StandardCopyOption
选项,实现例如覆盖目标文件的功能:// 使用 Files.copy 方法并指定覆盖选项 Files.copy(source.toPath(), dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
这个选项会在目标文件已经存在的情况下覆盖目标文件,避免抛出文件已存在的异常。
五.四种方法对比
为了找出哪种方法最快,我编写了一个测试类,并依次对上述方法进行了测试,用它们来复制一个 1 GB 的文件。每次调用时,我都使用了不同的文件,以避免由于缓存导致后续方法受益。
注意:以下代码中的文件的路径可以改为你自己的文件的路径
// 导入必要的类和包
import java.io.File; // 用于表示文件和目录路径名的抽象表示
import java.io.FileInputStream; // 用于从文件中读取数据的输入流
import java.io.FileOutputStream; // 用于向文件中写入数据的输出流
import java.io.IOException; // 用于处理输入/输出操作中的异常
import java.io.InputStream; // 输入流的通用父类
import java.io.OutputStream; // 输出流的通用父类
import java.nio.channels.FileChannel; // 用于文件通道的复制操作
import java.nio.file.Files; // Java 7 中引入的文件操作类
// 导入 Apache Commons IO 库中的工具类
import org.apache.commons.io.FileUtils; // 提供简单实用的文件操作方法
// 定义主类
public class JavaCopyFile {
// 主方法,程序从这里开始执行
public static void main(String[] args) throws InterruptedException, IOException {
// 定义源文件对象,表示要复制的文件
File source = new File("/Users/pankaj/tmp/source.avi");
// 定义目标文件对象,表示复制的目标位置
File dest = new File("/Users/pankaj/tmp/dest.avi");
// 使用流 (Stream) 方法复制文件
long start = System.nanoTime(); // 记录开始时间
copyFileUsingStream(source, dest); // 调用流方法复制文件
System.out.println("Time taken by Stream Copy = " + (System.nanoTime() - start)); // 打印流方法的执行时间
// 使用 FileChannel 方法复制文件
source = new File("/Users/pankaj/tmp/sourceChannel.avi"); // 定义新的源文件对象
dest = new File("/Users/pankaj/tmp/destChannel.avi"); // 定义新的目标文件对象
start = System.nanoTime(); // 记录开始时间
copyFileUsingChannel(source, dest); // 调用 FileChannel 方法复制文件
System.out.println("Time taken by Channel Copy = " + (System.nanoTime() - start)); // 打印 FileChannel 方法的执行时间
// 使用 Apache Commons IO 工具方法复制文件
source = new File("/Users/pankaj/tmp/sourceApache.avi"); // 定义新的源文件对象
dest = new File("/Users/pankaj/tmp/destApache.avi"); // 定义新的目标文件对象
start = System.nanoTime(); // 记录开始时间
copyFileUsingApacheCommonsIO(source, dest); // 调用 Apache Commons IO 方法复制文件
System.out.println("Time taken by Apache Commons IO Copy = " + (System.nanoTime() - start)); // 打印 Apache Commons IO 方法的执行时间
// 使用 Java 7 的 Files 类复制文件
source = new File("/Users/pankaj/tmp/sourceJava7.avi"); // 定义新的源文件对象
dest = new File("/Users/pankaj/tmp/destJava7.avi"); // 定义新的目标文件对象
start = System.nanoTime(); // 记录开始时间
copyFileUsingJava7Files(source, dest); // 调用 Files 类方法复制文件
System.out.println("Time taken by Java7 Files Copy = " + (System.nanoTime() - start)); // 打印 Files 类方法的执行时间
}
}
以下是上述程序的输出结果,每次只使用一种方法进行 Java 文件复制操作,需要把其他方法相关的代码给注释掉。
Time taken by Stream Copy = 44582575000
Time taken by Channel Copy = 104138195000
Time taken by Apache Commons IO Copy = 108396714000
Time taken by Java7 Files Copy = 89061578000
从输出结果可以看出,流复制(Stream Copy)是 Java 中复制文件的最佳方法。但这只是一个非常基础的测试。如果你正在开发一个对性能要求较高的项目,那么你应该尝试不同的文件复制方法,并记录它们的执行时间,以找到适合你项目的最佳方法。此外,你还可以根据文件的平均大小,尝试不同的 Java 文件复制方式来进行优化。
通过这篇文章,相信你已经掌握了 Java 文件复制的 4 种不同实现方式,并且了解了每种方法的适用场景和性能表现。无论是用于期末作业、毕业设计,还是实际项目开发,选择最优的复制方式,都能让你的代码更高效、更专业!💡
如果这篇文章对你有所帮助,不要忘记 点赞 👍、收藏 ⭐、关注 👀,你的支持是我们分享更多优质内容的动力!🔥 同时,欢迎浏览我们的其他文章,探索更多 Java 技术干货和项目优化技巧。💻 学习之路,你我同行,下一篇更精彩!🚀