File.listFiles 接收的 FilenameFilter 是函数式接口,仅含 accept(File dir, String name) 方法,其中 name 为纯文件名字符串(不含路径),常用于后缀匹配等轻量过滤。

File.listFiles 接收的 FilenameFilter 是什么类型?
File.listFiles 有两个重载版本,筛选用的是接收 FilenameFilter 或 FileFilter 的那个。匿名内部类最常配合 FilenameFilter 使用,它只有一个方法:accept(File dir, String name) —— 注意:第二个参数是**纯文件名字符串**,不含路径,也不含父目录信息。
这意味着你不能在 accept 里调用 new File(name).isFile(),因为 name 可能是相对路径片段或只是名字;更不能依赖 dir 参数去拼全路径再判断类型(虽然可行,但违背了该接口的设计意图)。
常见错误现象:accept 方法里误用 name.endsWith(".txt") 却没处理大小写,结果漏掉 README.TXT;或者写了 name.contains(".txt"),把 mytext.txt.bak 也混进来了。
怎么写一个安全的后缀匹配匿名类?
核心是用 name 做后缀判断,但要兼顾大小写和点号位置。推荐用 name.toLowerCase().endsWith(".txt"),而不是 name.endsWith(".txt") 或正则。
- 不推荐
name.matches(".*\.txt$"):正则开销大,且$在这里无意义(name本就是完整文件名) - 避免
name.substring(name.lastIndexOf(".") + 1).equals("txt"):会抛StringIndexOutOfBoundsException(当文件名不含.时) - 必须先检查
name是否包含.,否则lastIndexOf返回 -1,加 1 后变成 0,截取可能出错
实操建议:直接用 toLowerCase().endsWith() 最简明。例如筛选所有文本文件:
File[] txtFiles = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".txt") ||
name.toLowerCase().endsWith(".md") ||
name.toLowerCase().endsWith(".log");
}
});
为什么不用 FileFilter?它有什么不同?
FileFilter 的 accept(File file) 方法传入的是完整 File 对象,所以你可以调用 file.isFile()、file.length()、甚至 file.getName()。但它**无法区分“名字叫 .git”的文件和“.git”目录**——因为 file 对象本身不携带“这是目录还是文件”的元信息,除非你主动调用 isFile() 或 isDirectory()。
使用场景差异:
- 只看后缀?用
FilenameFilter更轻量,不触发 IO - 还要按大小、修改时间、是否可读筛选?用
FileFilter更自然 - 想同时过滤目录(比如跳过
.git)?FileFilter能直接!file.isDirectory(),而FilenameFilter根本看不到这个信息
性能影响:在大量小文件场景下,FilenameFilter 省去每次构造 File 对象的开销,略快;但实际差异通常可忽略。
Java 7+ 应该改用什么?
File.listFiles 是旧 API,不支持符号链接处理、无法中断、异常处理笨重。现代写法应优先用 Files.newDirectoryStream() 配合 Glob 或 lambda:
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir.toPath(), "*.{txt,md,log}")) {
for (Path entry : stream) {
if (Files.isRegularFile(entry)) {
System.out.println(entry.getFileName());
}
}
}
注意:*.{txt,md,log} 是 Glob 语法,不是正则;Windows 不区分大小写,但 Linux 下仍需手动转小写或用 **/*.txt 配合 Files.walk() + String::equalsIgnoreCase。
容易被忽略的一点:无论用哪种方式,listFiles 和 newDirectoryStream 都**不递归**。如果需要子目录里的文本文件,得自己遍历或改用 Files.walk() —— 但后者默认深度无限,生产环境务必设 maxDepth,否则遇到挂载点或循环软链会卡死。

















