File.listFiles 传入 null 或非目录/不可读文件时静默返回 null,需预检 isDirectory() 和 canRead();FilenameFilter.accept 应为纯函数,避免 IO 和异常吞没;推荐用 Files.walk 替代。

File.listFiles 接收的 Filer 参数必须非 null,否则直接返回 null
很多开发者调用 listFiles 时传入自定义 FilenameFilter,但没注意其 accept 方法里对 file 参数做了空判断或路径操作,导致 NPE 或逻辑错乱。Java 的 File 对象本身不校验路径是否存在或是否为目录,listFiles 在传入的 File 不是目录、不可读、或 I/O 错误时,都直接返回 null —— 这不是异常,而是静默失败。
实操建议:
- 调用前先用
file.isDirectory()和file.canRead()做预检 -
FilenameFilter.accept(File dir, String name)中的dir是原始File对象(即你传给listFiles的那个),name是不带路径的纯文件名字符串,别试图在accept里 newFile(dir, name)再调isFile()—— 这会额外触发磁盘访问,且在并发或挂载异常时放大风险 - 后缀判断请统一用
name.toLowerCase().endsWith(".txt"),避免大小写敏感问题;不要用name.substring(name.lastIndexOf(".") + 1),它在无点文件名或以点结尾时会抛StringIndexOutOfBoundsException
用匿名类实现后缀过滤时,accept 方法不能捕获或吞掉异常
常见错误是在 accept 里写日志、调用外部服务,甚至 try-catch 所有异常并 return false —— 这会让 listFiles 把本该被筛选掉的文件也漏掉,且完全无提示。
实操建议:
-
accept必须是纯函数:只基于dir和name两个参数做判断,不修改状态、不 IO、不抛检查异常 - 如果需要日志调试,只打 info 级别,并确保不影响 return 值;例如:
if (name.endsWith(".log")) { logger.debug("matched: {}", name); return true; } - 想支持多个后缀?别硬编码 if-else,用
Set<string> extensions = Set.of(".jpg", ".png", ".gif")</string>配合extensions.contains(name.substring(name.lastIndexOf(".").toLowerCase()),注意处理无扩展名情况
替代方案:用 Files.walk + Path.getFileName().toString() 更可靠
File.listFiles 是旧 API,不支持深度遍历、符号链接控制、或流式处理;遇到大目录容易 OOM,且无法区分“访问拒绝”和“不存在”。JDK 7+ 推荐用 Files.walk。
实操建议:
- 基础用法:
try (Stream<Path> stream = Files.walk(dirPath, 1)) { return stream.filter(p -> p.isRegularFile() && p.getFileName().toString().toLowerCase().endsWith(".xml")) .toArray(Path[]::new); } -
walk第二个参数是最大深度,设为1等价于listFiles;设为Integer.MAX_VALUE则递归全查 - 注意
Files.walk抛出的是IOException,必须捕获或声明;而listFiles出错只返回 null,更难定位 - 如果目标是收集文件内容而非路径,直接用
Files.lines(p)或Files.readString(p),避免手动 newFileInputStream
Android 上 File.listFiles 可能因存储权限返回空数组而非 null
Android 10+ 强制启用分区存储(Scoped Storage),即使你有 READ_EXTERNAL_STORAGE 权限,Environment.getExternalStorageDirectory() 下的子目录也可能被屏蔽。此时 listFiles 不抛异常、不返回 null,而是返回空 File[] —— 表面看像没文件,实则是权限拦截。
实操建议:
- 优先使用
getExternalFilesDir()或getFilesDir(),这些路径无需运行时权限 - 若必须访问公共目录(如 DCIM),改用
MediaStore查询,或申请MANAGE_EXTERNAL_STORAGE(仅限特定场景,审核严格) - 调试时加一句
Log.d("List", "result length: " + (files == null ? "null" : files.length)),区分 null 和 empty
File 类在路径处理上留了很多隐式假设,比如默认编码、分隔符、符号链接行为。哪怕只是筛个后缀,listFiles 的返回值语义(null / empty / 正常数组)就依赖至少三个系统级条件:目录存在性、可读性、底层文件系统响应。别把它当工具函数用,得当成一次微型系统调用来看。

















