在读2.2节关于路径的内容时,你可能已经猜到目录不过是带有特别属性的Path
。遍历目录的能力是Java 7引人注目的新特性。新加入的java.nio.file.DirectoryStream<T>
接口和它的实现类提供了很多功能:
- 循环遍历目录中的子项,比如查找目录中的文件;
- 用glob表达式1(比如
*Foobar*
)进行目录子项匹配和基于MIME的内容检测(比如text/xml
文件); - 用
walkFileTree
方法实现递归移动、复制和删除操作。
本节主要讨论两个常见用例:在一个目录中查找文件以及在目录树中执行相同的任务。我们先从最简单的开始:在一个目录中查找任意文件。
1 glob通常指有限的模式匹配,源自早期Unix中的glob库函数,该函数用于查找文件系统中指定模式的路径,虽然所用语法和正则表达式类似,但没有正则表达式那么强大的表达力,详见附录B。——译者注
2.3.1 在目录中查找文件
我们先讨论一个简单的例子,用模式匹配过滤出java7developer项目中所有的.properties文件。请看下面的代码:
代码清单2-2 列出目录下的properties文件
Path dir = Paths.get(\"C:\\workspace\\java7developer\");//①设定起始路径
try(DirectoryStream<Path> stream
= Files.newDirectoryStream(dir, \"*.properties\")){//②声明过滤流
//③ 找出所有.properties 文件并输出
for (Path entry: stream)
{
System.out.println(entry.getFileName);
}
}
catch (IOException e)
{
System.out.println(e.getMessage);
}
最前面是我们已经熟悉的Paths.get(String)
调用①。紧随其后的是关键方法DirectoryStream(Path directory, String patternMatch)
②,它返回一个经过过滤的DirectoryStream
,其中包含以.properties结尾的文件。最后输出每个子项③。
过滤流中用到的模式匹配称为glob模式匹配,它和Perl正则表达式类似,但稍有不同。附录B中有如何使用glob模式匹配的详细说明。
代码清单2-2展示了新API处理单个目录的能力,但如果需要递归过滤多个目录时该怎么办?
2.3.2 遍历目录树
Java 7支持整个目录树的遍历。也就是说你可以很容易地搜寻目录树中的文件,在子目录中查找,并对它们执行操作。比如你可能想在做开发的机器上弄个工具类来删除目录/opt/workspace/java下的所有.class文件,完成构建前的清除工作。
遍历目录树是Java 7的新特性,要想正确使用,你得掌握一些接口及其实现的细节。其中的关键方法是:
Files.walkFileTree(Path startingDir, FileVisitor<? superPath> visitor);
提供startingDir
非常简单,但给出FileVisitor
接口的实现类就比较麻烦了(参数FileVisitor<? superPath> visitor
看上去就不是善茬儿),因为最少得实现下面5个方法(T
一般就是Path
):
FileVisitResult preVisitDirectory(T dir)
FileVisitResult preVisitDirectoryFailed(T dir, IOException exc)
FileVisitResult visitFile(T file, BasicFileAttributes attrs)
FileVisitResult visitFileFailed(T file, IOException exc)
FileVisitResult postVisitDirectory(T dir, IOException exc)
看起来挺吓人的吧?好在Java 7 API的设计者们已经提供了一个默认实现类,SimpleFileVisitor<T>
。
我们要扩展并修改代码清单2-2。下面的代码会列出C:workspacejava7developersrc目录下及其子目录内的所有.java文件。这段代码展示了Files.walkFileTree
方法对默认实现类SimpleFileVisitor
的用法,用一个特定的visitFile
方法实现来改进它。
代码清单2-3 列出子目录下的所有java源码文件
public class Find
{
public static void main(String args) throws IOException
{
Path startingDir =
Paths.get(\"C:\\workspace\\java7developer\\src\");//设置起始目录
Files.walkFileTree(startingDir,
new FindJavaVisitor); //①调用walkFileTree
}
private static class FindJavaVisitor
extends SimpleFileVisitor<Path> //②扩展SimpleFileVisitor<Path>
{
@Override
public FileVisitResult
visitFile(Path file, BasicFileAttributes attrs) //③ 重写visitFile方法
{
if (file.toString.endsWith(\".java\")) {
System.out.println(file.getFileName);
}
return FileVisitResult.CONTINUE;
}
}
}
整个过程从调用Files.walkFileTree
方法开始①。这里的关键是 FindJavaVisitor
,该类扩展了FileVisitor
的默认实现类SimpleFileVisitor
②。你想让SimpleFileVisitor
来完成大部分工作,比如遍历目录。可实际上你唯一要做的就是重写visitFile(Path,BasicFileAttributes)
1方法③,在这个方法中你也只需要写些代码来判断文件名是否以.java结尾,如果确实是,就在stdout中输出。
1 2.4节会介绍BasicFileAttributes
,所以暂时不用管它。
其他用例包括递归移动、复制、删除或者修改文件。在大多数应用场景中,你只需要扩展SimpleFileVisitor
。但如果你想实现自己的FileVisitor
,API也很灵活。
注意 为了确保递归等操作的安全性,
walkFileTree
方法不会自动跟随符号链接。如果你确实需要跟随符号链接,就需要检查那个属性(如2.4.3节所述)并执行相应的操作。
现在你对路径和目录树已经熟悉了,该从处理位置进入真正的文件系统操作上了,接下来我们会向你介绍新的Files
类及它的朋友们。