1. 概述

在Java中, PathFile 是负责文件I/O操作的类。它们执行相同的功能但属于不同的包。

在本教程中,我们将讨论这两个类之间的差异。我们将从快速回顾课程开始。然后,我们将讨论一些遗留的缺点。最后,我们将学习如何在两个 API 之间迁移功能。

2. java.io.File

从第一个版本开始,Java 就提供了自己的 java.io 包,其中几乎包含我们执行输入和输出操作可能需要的所有类。 File文件和目录路径名的抽象表示

File file = new File("baeldung/tutorial.txt");

File 类的实例是不可变的——一旦创建,该对象表示的抽象路径名就永远不会改变。

3. java.nio.file.Path

PathNIO2更新的一部分,该更新随版本 7 一起出现在 Java 中。它提供了 一个全新的 API 来处理 I/O 。此外,与旧版 File 类一样, Path 还创建 一个可用于在文件系统中定位文件的对象

同样,它可以执行 File 类可以完成的 所有操作

Path path = Paths.get("baeldung/tutorial.txt");

我们没有像使用 File API 那样使用构造函数,而是使用静态 java.nio.file.Paths.get() 方法创建 Path 实例。

4. 文件 类的缺点

在简短回顾这两个类之后,现在让我们讨论这两个 API 并回答以下问题:如果它们提供相同的功能, 为什么 Oracle 决定引入新的 API,以及我应该使用哪一个?

众所周知, java .io 包是随 Java JDK 的第一个版本一起提供的,允许我们执行 I/O 操作。从那时起,许多开发人员报告了它的许多缺点、功能缺失以及某些功能的问题。

4.1.错误处理

最常见的问题是错误处理能力差。许多方法 不会告诉我们所遇到问题的任何细节 ,甚至根本不会抛出异常。

假设我们有一个删除文件的简单程序:

File file = new File("baeldung/tutorial.txt");
boolean result = file.delete();

这段代码编译并成功运行,没有任何错误。当然,我们有一个包含 错误的结果 标志,但我们不知道此失败的原因。该文件可能不存在,或者程序可能没有删除它的权限。

我们现在可以使用更新的 NIO2 API 重写相同的功能:

Path path = Paths.get("baeldung/tutorial.txt");
Files.delete(path);

现在,编译器要求我们处理 IOException 。此外,抛出的异常还包含有关其失败的详细信息,例如,该文件是否不存在。

4.2.元数据支持

java.io 包中的 File 类对元数据的支持很差,这会导致跨平台的I/O操作需要有关文件的元信息的问题。

元数据还可以包括权限、文件所有者和安全属性。因此, File 类根本 不支持符号链接 ,并且 rename() 方法在不同平台上的工作方式也不一致

4.3.方法扩展和性能

由于 File 类的方法不可扩展,因此还存在性能问题。它会导致某些包含大量文件的目录出现问题。列出目录的内容 可能会导致挂起,从而导致内存资源问题 。最后,它可能导致拒绝服务。

由于其中一些缺点,Oracle 开发了改进的 NIO2 API。开发人员 应该尽可能使用这个新的 java.nio 包而不是遗留类来启动新项目

5. 映射功能

为了修复 java.io 包中的一些缺陷,Oracle准备了自己的缺陷总结,帮助开发人员在API之间进行迁移。

NIO2 包提供了所有遗留功能,包括针对上述缺点的改进。由于大量应用程序可能仍在使用此旧 API,Oracle 目前 不打算在未来版本中弃用或删除旧 API

在新的 API 中,我们使用 java.nio.file.Files 类中的 静态方法,而不是实例方法 。现在让我们快速比较一下这些 API。

5.1. 文件路径 实例

当然,主要区别在于包和类名:

java.io.File file = new java.io.File("baeldung/tutorial.txt");
java.nio.file.Path path = java.nio.file.Paths.get("baeldung/tutorial.txt");

这里,我们通过构造函数构建一个 File 对象,同时使用静态方法获取 Path 。我们还可以使用多个参数解析复杂路径:

File file = new File("baeldung", "tutorial.txt");
Path path = Paths.get("baeldung", "tutorial.txt");

并且,我们可以通过链接 resolve() 方法来达到相同的结果:

Path path2 = Paths.get("baeldung").resolve("tutorial.txt");

此外,我们可以使用 toPath()toFile() 方法在 API 之间转换对象:

Path pathFromFile = file.toPath();
File fileFromPath = path.toFile();

5.2.管理文件和目录

这两个 API 都提供了管理文件和目录的方法。我们将使用之前创建的实例对象来演示这一点。

要创建文件 ,我们可以使用 createNewFile()Files.createFile() 方法:

boolean result = file.createNewFile();
Path newPath = Files.createFile(path);

要创建目录 ,我们需要使用 mkdir()Files.createDirectory()

boolean result = file.mkdir();
File newPath = Files.createDirectory(path);

这些方法还有其他变体,可以通过 mkdirs()Files.createDirectories() 方法 包含所有不存在的子目录

boolean result = file.mkdirs();
File newPath = Files.createDirectories(path);

当我们想要 重命名或移动文件 时,我们需要创建另一个实例对象并使用 renameTo()Files.move()

boolean result = file.renameTo(new File("baeldung/tutorial2.txt"));
Path newPath = Files.move(path, Paths.get("baeldung/tutorial2.txt"));

要执行删除操作 ,我们使用 delete()Files.delete()

boolean result = file.delete();
Files.delete(Paths.get(path));

请注意,如果出现任何错误,旧方法会返回一个结果设置为 false 的标志。 NIO2 方法返回一个新的 Path 实例,但删除操作除外,当发生错误时会抛出 IOException

5.3.读取元数据

我们还可以获得文件的一些基本信息,例如权限或类型。和以前一样,我们需要一个实例对象:

// java.io API
boolean fileExists = file.exists();
boolean fileIsFile = file.isFile();
boolean fileIsDir = file.isDirectory();
boolean fileReadable = file.canRead();
boolean fileWritable = file.canWrite();
boolean fileExecutable = file.canExecute();
boolean fileHidden = file.isHidden();

// java.nio API
boolean pathExists = Files.exists(path);
boolean pathIsFile = Files.isRegularFile(path);
boolean pathIsDir = Files.isDirectory(path);
boolean pathReadable = Files.isReadable(path);
boolean pathWritable = Files.isWritable(path);
boolean pathExecutable = Files.isExecutable(path);
boolean pathHidden = Files.isHidden(path);

5.4.路径名方法

最后,让我们快速浏览一下 File 类中用于获取文件系统路径的方法。请注意,与前面的示例不同,它们中的大多数都是直接在对象实例上执行的。

要获取绝对路径或规范路径 ,我们可以使用:

// java.io API
String absolutePathStr = file.getAbsolutePath();
String canonicalPathStr = file.getCanonicalPath();

// java.nio API
Path absolutePath = path.toAbsolutePath();
Path canonicalPath = path.toRealPath().normalize();

虽然 Path 对象是不可变的,但它返回一个新实例。此外,NIO2 API 具有 toRealPath()normalize() 方法,我们可以使用它们来删除冗余。

可以使用 toUri() 方法 转换为 URI

URI fileUri = file.toURI();
URI pathUri = path.toUri();

另外,我们可以 列出目录内容

// java.io API
String[] list = file.list();
File[] files = file.listFiles();

// java.nio API
DirectoryStream<Path> paths = Files.newDirectoryStream(path);

NIO2 API 返回自己的 DirectoryStream 对象,该对象实现了 Iterable 接口。

六,结论

从 Java 7 开始,开发人员现在可以在两个 API 之间进行选择来处理文件。在本文中,我们讨论了与 java.io.File 类相关的一些不同的缺点和问题。

为了解决这些问题,Oracle 决定提供 NIO 包,它带来了相同的功能并进行了大量改进

然后,我们回顾了这两个 API。通过示例,我们学习了如何在它们之间进行迁移。我们还了解到, java.io.File 现在被视为遗留文件,不建议用于新项目 。但是,没有计划弃用和删除它。

与往常一样,本文中的所有代码片段都可以在 GitHub 上获取