java nio.2中的java.nio.file.files类提供了丰富的文件操作功能,分为四大类。1. 文件与目录的创建、删除与移动:createfile、createdirectory、createdirectories用于创建文件或目录;delete和deleteifexists用于删除;copy和move用于复制和移动。2. 文件内容读写:readallbytes和readalllines用于快速读取文件内容;write用于写入字节或文本。3. 文件属性与状态查询:exists、isdirectory、isregularfile等方法检查状态;size、getlastmodifiedtime获取属性;readattributes批量获取详细属性信息。4. 文件系统遍历:list列出目录内容;walk递归遍历目录树;find在遍历中按条件过滤文件。这些方法结合stream api使文件处理更简洁高效,适用于各类文件系统操作场景。

java.nio.file.Files是Java NIO.2中一个非常核心的工具类,它提供了大量静态方法,用于执行文件和目录的各种操作。简单来说,它就是我们日常处理文件系统时最常用、也最强大的帮手,覆盖了从创建、删除、复制、移动到读写、属性查询乃至文件系统遍历的方方面面。它的设计理念比老旧的java.io.File更现代,更注重异常处理和性能,也更好地融入了Java 8以来的Stream API。

Files类的方法非常多,但我们可以从功能上大致分为几类,这样理解起来会清晰很多。我平时用得最多的,不外乎就是那些对文件或目录进行生命周期管理、内容读写以及属性查询的操作。

1. 文件与目录的创建、删除与移动
这是最基础也最常用的。

createFile(Path path, FileAttribute<?>... attrs): 创建一个空文件。如果文件已存在,会抛出FileAlreadyExistsException。createDirectory(Path dir, FileAttribute<?>... attrs): 创建一个新目录。父目录必须存在。createDirectories(Path dir, FileAttribute<?>... attrs): 创建目录,如果父目录不存在,也会一并创建。这个非常实用,省去了我们手动检查和创建父目录的麻烦。delete(Path path): 删除文件或空目录。如果文件或目录不存在,或者目录不为空,会抛出异常。deleteIfExists(Path path): 删除文件或空目录,如果不存在则不执行任何操作,也不会抛出异常。我个人更喜欢用这个,省心。copy(Path source, Path target, CopyOption... options): 复制文件或目录。可以指定复制选项,比如StandardCopyOption.REPLACE_EXISTING(覆盖目标文件)或StandardCopyOption.COPY_ATTRIBUTES(复制文件属性)。move(Path source, Path target, CopyOption... options): 移动或重命名文件或目录。和copy类似,也有各种选项。2. 文件内容的读写
Files提供了一些非常方便的方法来快速读写文件内容,尤其适合处理小到中等大小的文件。
readAllBytes(Path path): 读取文件所有字节到byte[]数组。readAllLines(Path path, Charset cs): 读取文件所有行到List<String>。这个方法简直是文本处理的利器,一行代码搞定读取,省去了手动循环读取行的麻烦。write(Path path, byte[] bytes, OpenOption... options): 将字节数组写入文件。write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options): 将字符串集合(比如List<String>)逐行写入文件。3. 文件属性与状态查询
在进行操作前,我们经常需要检查文件或目录的状态。
exists(Path path, LinkOption... options): 检查文件或目录是否存在。notExists(Path path, LinkOption... options): 检查文件或目录是否不存在。isDirectory(Path path, LinkOption... options): 检查是否是目录。isRegularFile(Path path, LinkOption... options): 检查是否是普通文件。isReadable(Path path): 检查文件是否可读。isWritable(Path path): 检查文件是否可写。isExecutable(Path path): 检查文件是否可执行。size(Path path): 获取文件大小(字节)。getLastModifiedTime(Path path, LinkOption... options): 获取文件最后修改时间。readAttributes(Path path, Class<A> type, LinkOption... options): 读取文件属性集。这个方法非常强大,可以获取文件的各种详细属性,比如BasicFileAttributes。4. 文件系统遍历
对于需要处理整个目录树的场景,Files也提供了强大的流式API。
list(Path dir): 列出目录下的直接子文件和子目录,返回Stream<Path>。walk(Path start, int maxDepth, FileVisitOption... options): 深度优先遍历目录树,返回Stream<Path>。可以指定遍历深度和选项。find(Path start, int maxDepth, BiPredicate<Path, BasicFileAttributes> matcher, FileVisitOption... options): 遍历目录树,并根据提供的匹配器进行过滤,返回Stream<Path>。这些方法基本涵盖了日常文件操作的绝大部分需求。我个人觉得,熟练掌握这些,基本就能在Java里自如地玩转文件系统了。
谈到文件读写,特别是文本文件,Files类提供了几种非常“Java 8”风格的优雅方式。我个人在处理一些配置文件或者日志分析时,特别偏爱用这些方法,它们能让代码看起来非常简洁。
最直接的当然是上面提到的readAllLines(Path path, Charset cs)和write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options)。比如,你想读取一个UTF-8编码的文件,然后把其中包含特定字符串的行筛选出来,再写入另一个文件:
Path source = Paths.get("input.txt");
Path target = Paths.get("output.txt");
Charset utf8 = StandardCharsets.UTF_8;
try {
List<String> filteredLines = Files.readAllLines(source, utf8)
.stream()
.filter(line -> line.contains("important_keyword"))
.collect(Collectors.toList());
Files.write(target, filteredLines, utf8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
System.out.println("Filtered lines written to " + target);
} catch (IOException e) {
System.err.println("Error processing file: " + e.getMessage());
}这段代码,从读取到过滤再到写入,一气呵成,没有冗余的BufferedReader、BufferedWriter的创建和关闭,内部都帮你处理好了。但这里有个需要注意的点:readAllLines会一次性把所有内容加载到内存。对于特别大的文件(比如几个GB),这样做可能会导致内存溢出。
这时候,Files.lines(Path path, Charset cs)就派上用场了。它返回一个Stream<String>,是惰性加载的,只有当你真正消费这个流的时候,才会去读取文件内容。这对于处理大文件来说,是更内存友好的选择。
Path largeFile = Paths.get("large_log.txt");
Path errorLog = Paths.get("error_log.txt");
try (Stream<String> lines = Files.lines(largeFile, utf8)) {
List<String> errorLines = lines.filter(line -> line.contains("ERROR"))
.limit(100) // 比如只取前100条错误
.collect(Collectors.toList());
Files.write(errorLog, errorLines, utf8);
System.out.println("Extracted error lines to " + errorLog);
} catch (IOException e) {
System.err.println("Error reading large file: " + e.getMessage());
}看到没,try-with-resources配合Files.lines,既保证了资源自动关闭,又提供了流式处理的能力,简直是完美组合。我个人觉得,这种方式才是真正意义上的“优雅”,它不仅让代码简洁,更重要的是,它考虑到了性能和资源管理。
文件系统管理,不仅仅是创建删除那么简单,更多时候我们需要了解文件的“身份信息”——它的类型、大小、修改时间,甚至更底层的权限信息。Files类在这方面提供了非常细致且强大的支持,远超java.io.File。
首先,一切都围绕着Path对象展开。Path是NIO.2中表示文件或目录路径的核心抽象,你可以用Paths.get("some/path/to/file.txt")来创建它。有了Path,Files类的各种方法才能施展拳脚。
例如,我们想检查一个路径到底是个文件还是目录,或者它是否存在:
Path myPath = Paths.get("/tmp/mydata"); // 假设这个路径可能存在也可能不存在,可能是文件也可能是目录
if (Files.exists(myPath)) {
System.out.println(myPath + " exists.");
if (Files.isDirectory(myPath)) {
System.out.println(myPath + " is a directory.");
} else if (Files.isRegularFile(myPath)) {
System.out.println(myPath + " is a regular file.");
try {
System.out.println("Size: " + Files.size(myPath) + " bytes.");
System.out.println("Last Modified: " + Files.getLastModifiedTime(myPath));
} catch (IOException e) {
System.err.println("Could not get file info: " + e.getMessage());
}
}
} else {
System.out.println(myPath + " does not exist.");
}这些方法简单直观,但真正强大的在于readAttributes。当你需要获取文件的一组属性时,比如创建时间、最后访问时间、文件所有者等,readAttributes就能派上用场。它返回一个实现了BasicFileAttributes接口的对象,或者更具体的属性集接口,比如DosFileAttributes(针对Windows系统)或PosixFileAttributes(针对Unix/Linux系统)。
Path someFile = Paths.get("important_document.pdf");
try {
BasicFileAttributes attrs = Files.readAttributes(someFile, BasicFileAttributes.class);
System.out.println("Is directory? " + attrs.isDirectory());
System.out.println("Is regular file? " + attrs.isRegularFile());
System.out.println("File size: " + attrs.size() + " bytes");
System.out.println("Creation time: " + attrs.creationTime());
System.out.println("Last access time: " + attrs.lastAccessTime());
System.out.println("Last modified time: " + attrs.lastModifiedTime());
} catch (IOException e) {
System.err.println("Error reading attributes: " + e.getMessage());
}这种方式比你一个个调用size()、getLastModifiedTime()要高效,因为它通常一次性从文件系统读取所有请求的属性。在需要批量获取文件信息时,这能显著减少与文件系统的交互次数,提升性能。我个人在做文件同步或者备份工具的时候,就经常用到这些属性查询方法,它们是判断文件是否需要处理的重要依据。
遍历文件系统,这可是个大活儿,尤其是当你需要处理一个复杂的目录结构时。在Java NIO.2之前,我们通常需要自己写递归函数来遍历,既繁琐又容易出错。Files类彻底改变了这一点,它引入了流式API来简化文件系统遍历,让这个过程变得异常优雅和高效。
最常用的三个方法是list()、walk()和find()。
Files.list(Path dir): 这个方法非常直接,它只列出指定目录下的直接子文件和子目录,不进行递归。返回的是一个Stream<Path>。这就像你打开一个文件夹,看到里面有什么,仅此而已。
Path currentDir = Paths.get("."); // 当前目录
try (Stream<Path> entries = Files.list(currentDir)) {
entries.forEach(System.out::println);
} catch (IOException e) {
System.err.println("Error listing directory: " + e.getMessage());
}这个用法很像ls命令,简单明了。
Files.walk(Path start, int maxDepth, FileVisitOption... options): 这才是真正的“大杀器”,它能递归地遍历从start路径开始的整个目录树。你可以指定maxDepth来限制遍历的深度,比如1就和list差不多了,Integer.MAX_VALUE就是遍历所有子目录。它也返回一个Stream<Path>,包含了遍历到的所有文件和目录。
我第一次用Files.walk的时候,简直惊呆了,以前需要写一堆递归逻辑才能实现的功能,现在几行代码就能搞定,效率提升不是一点半点。比如,你想找出某个目录下所有.java文件:
Path projectRoot = Paths.get("/path/to/my/java/project");
try (Stream<Path> javaFiles = Files.walk(projectRoot)) {
javaFiles.filter(Files::isRegularFile) // 确保是文件
.filter(p -> p.toString().endsWith(".java")) // 过滤出.java文件
.forEach(System.out::println);
} catch (IOException e) {
System.err.println("Error walking directory: " + e.getMessage());
}这里,Files.walk返回的流包含了目录和文件,所以我们通常会用Files::isRegularFile先过滤一下。
Files.find(Path start, int maxDepth, BiPredicate<Path, BasicFileAttributes> matcher, FileVisitOption... options): find比walk更进一步,它在遍历的同时,允许你提供一个BiPredicate(一个接受Path和BasicFileAttributes的函数),直接在遍历过程中进行过滤。这对于需要根据文件属性(如大小、修改时间)来查找文件时非常有用。
比如,查找所有大小超过1MB,且在过去24小时内修改过的PDF文件:
Path searchDir = Paths.get("/path/to/documents");
long oneDayAgo = System.currentTimeMillis() - (24 * 60 * 60 * 1000);
try (Stream<Path> foundFiles = Files.find(searchDir, Integer.MAX_VALUE,
(path, attrs) -> attrs.isRegularFile() &&
attrs.size() > (1024 * 1024) && // 1MB
attrs.lastModifiedTime().toMillis() > oneDayAgo &&
path.toString().endsWith(".pdf"))) {
foundFiles.forEach(System.out::println);
} catch (IOException e) {
System.err.println("Error finding files: " + e.getMessage());
}find的强大之处在于,它将遍历和过滤逻辑紧密结合,代码更紧凑,也更易读。在使用这些流时,记得使用try-with-resources来确保流的正确关闭,避免资源泄露,这在处理文件系统资源时尤为重要。这些高级遍历方法,无疑让Java在文件系统操作方面达到了一个新的高度,用起来真是得心应手。
以上就是Files的常用方法都有哪些?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号