Java – Path vs File – Java – 路径与文件

最后修改: 2021年 6月 13日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In Java, Path and File are classes responsible for file I/O operations. They perform the same functions but belong to different packages.

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

In this tutorial, we’ll discuss the differences between these two classes. We’ll start with a quick class recap. Then, we’ll talk about some legacy drawbacks. Finally, we’ll learn how to migrate functionalities between both APIs.

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

2. java.io.File Class

2、java.io.File

Since the very first versions, Java has delivered its own java.io package, which contains nearly every class we might ever need to perform input and output operations. The File class is an abstract representation of file and directory pathnames:

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

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

Instances of the File class are immutable – once created, the abstract pathname represented by this object will never change.

文件类的实例是不可改变的–一旦创建,这个对象所代表的抽象路径名将永远不会改变。

3. java.nio.file.Path Class

3、java.nio.file.Path

The Path class forms part of the NIO2 update, which came to Java with version 7. It delivers an entirely new API to work with I/O. Moreover, like the legacy File class, Path also creates an object that may be used to locate a file in a file system.

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

Likewise, it can perform all the operations that can be done with the File class:

同样地,它可以执行所有可以用文件类进行的操作

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

Instead of using a constructor as we do with the File API, we create a Path instance using the static java.nio.file.Paths.get() method.

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

4. File Class Drawbacks

4.文件类的弊端

After this short recap of the two classes, let’s now discuss both APIs and answer the question: If they deliver the same functionality, why did Oracle decide to bring a new API, and which one should I use?

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

As we know, the java.io package was delivered with the first release of the Java JDK, allowing us to perform I/O actions. Since then, many developers have reported many drawbacks, missing functionality, and problems with some of its capabilities.

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

4.1. Error Handling

4.1 错误处理

The most common problem is poor error handling. Many methods don’t tell us any details about the encountered problem or even throw exceptions at all.

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

Let’s assume we have a simple program that deletes a file:

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

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

This code compiles and runs successfully without any error. Of course, we have a result flag containing a false value, but we don’t know the reason for this failure. The file might not exist, or the program may not have permission to delete it.

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

We can now rewrite the same functionality using the newer NIO2 API:

我们现在可以使用较新的NIO2 API重写同样的功能。

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

Now, the compiler requires us to handle an IOException. Moreover, a thrown exception has details about its failure that will tell you, for example, if the file does not exist.

现在,编译器要求我们处理一个IOException。此外,一个被抛出的异常有关于其失败的细节,会告诉你,例如,文件是否不存在。

4.2. Metadata Support

4.2. 元数据支持

The File class in the java.io package has poor metadata support, which leads to problems across different platforms with I/O operations requiring meta-information about files.

java.io包中的File类对元数据的支持很差,这导致不同平台上的I/O操作需要文件的元信息时出现问题。

The metadata may also include permissions, file owner, and security attributes. Due to this, the File class doesn’t support symbolic links at all, and the rename() method doesn’t work consistently across different platforms.

元数据还可能包括权限、文件所有者和安全属性。由于这个原因,文件根本不支持符号链接,而且rename()方法在不同平台上的工作并不一致

4.3. Method Scaling and Performance

4.3.方法扩展和性能

There is also a performance problem because the methods of the File class don’t scale. It leads to problems with some directories with a large number of files. Listing the contents of a directory could result in a hang, causing memory resource problems. Finally, it could lead to having a Denial of Service.

还有一个性能问题,因为File类的方法不能扩展。这导致了一些有大量文件的目录的问题。列出一个目录的内容可能会导致挂起,造成内存资源问题。最后,它可能导致有拒绝服务。

Due to some of these drawbacks, Oracle developed the improved NIO2 API. Developers should start new projects using this new java.nio package instead of legacy classes, where possible.

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

5. Mapping Functionality

5.绘图功能

In order to fix some gaps in the java.io package, Oracle prepared its own drawbacks summary, helping developers to migrate between APIs.

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

The NIO2 package delivers all the legacy functionalities, including improvements for the drawbacks mentioned. Due to a large number of applications that might still use this legacy API, Oracle currently doesn’t plan to deprecate or remove the old API in future releases.

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

In the new API, instead of instance methods, we use static ones from the java.nio.file.Files class. Let’s now quickly compare those APIs.

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

5.1. File and Path Instances

5.1.文件路径实例

The major difference is, of course, the package and class name:

当然,主要区别在于包和类的名称。

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

Here, we build a File object via the constructor, while we obtain a Path by using a static method. We can also resolve complex paths using multiple arguments:

在这里,我们通过构造函数建立一个文件对象,而我们通过使用静态方法获得一个路径。我们还可以使用多个参数来解决复杂的路径。

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

And, we can achieve the same result by chaining the resolve() method:

而且,我们可以通过链式的resolve()方法实现同样的结果。

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

Moreover, we can convert objects between APIs using toPath() and toFile() methods:

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

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

5.2. Managing Files and Directories

5.2.管理文件和目录

Both APIs deliver methods to manage files and directories. We’ll demonstrate this using the previously created instance objects.

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

To create files, we can use the createNewFile() and Files.createFile() methods:

为了创建文件,我们可以使用createNewFile()Files.createFile()方法。

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

To create a directory, we need to use mkdir() or Files.createDirectory():

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

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

There are additional variants of those methods to include all non-existing subdirectories, via the mkdirs() and Files.createDirectories() methods:

这些方法还有一些变体,通过mkdirs()Files.createDirectories()方法,包括所有不存在的子目录

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

When we want to rename or move a file, we need to create another instance object and use renameTo() or Files.move():

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

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

To perform a delete operation, we use delete() or Files.delete():

为了执行一个删除操作,我们使用delete()Files.delete()

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

Notice that legacy methods return a flag with a result set to false in case of any errors. NIO2 methods return a new Path instance, except for the delete operation, which throws an IOException when errors occur.

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

5.3. Reading Metadata

5.3.读取元数据

We can also obtain some basic information about files, such as permissions or types. As before, we need an instance object:

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

// 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. Pathname Methods

5.4 路径名方法

In the end, let’s quickly look at methods in the File class for getting the filesystem path. Be aware that, unlike the previous examples, most of them are performed directly on object instances.

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

To get absolute or canonical paths, we can use:

为了获得绝对或规范路径,我们可以使用。

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

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

While the Path object is immutable, it returns a new instance. Moreover, the NIO2 API has the toRealPath() and normalize() methods that we can use to remove redundancies.

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

Conversion to URI can be done by using the toUri() methods:

转换为URI可以通过使用toUri()方法完成。

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

Also, we can list the directory content:

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

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

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

NIO2 API returns its own DirectoryStream object, which implements the Iterable interface.

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

6. Conclusion

6.结论

Since Java 7, developers can now choose between two APIs to work with files. In this article, we discussed some of the different drawbacks and problems related to the java.io.File class.

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

To fix them, Oracle decided to deliver the NIO package, which brings the same functionality with massive improvements.

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

Then, we reviewed both APIs. Through examples, we learned how to migrate between them. We also learned that the java.io.File is now considered as legacy, and not recommended for new projects. However, there’s no plan to deprecate and remove it.

然后,我们回顾了这两个API。通过实例,我们了解了如何在它们之间进行迁移。我们还了解到,java.io.File现在被认为是遗留问题,不建议用于新项目。然而,目前还没有计划将其废弃和删除。

As always, all code snippets from this article are available over on GitHub.

一如既往,本文中的所有代码片段都可以在GitHub上找到