Java:如何在FileVisitor.preVisitDirectory()中访问外部作用域变量
解决FileVisitor外部作用域变量赋值问题及.txt文件内容收集
问题说明
在使用FileVisitor递归遍历目录时,重写preVisitDirectory方法后,尝试将方法内的dir参数赋值到外部作用域的String变量,出现enclosing scope编译错误。最终目标是将遍历到的所有.txt文件内容写入一个String中。
原始代码
FileVisitor<Path> simpleFileVisitor = new SimpleFileVisitor<Path>() { String cur_dir = ""; FileVisitor<Path> simpleFileVisitor = new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir,BasicFileAttributes attrs) throws IOException { // System.out.println("DIRECTORY NAME:"+ dir.getFileName() // + " LOCATION:"+ dir.toFile().getPath()); cur_dir = dir.toFile().getPath(); System.out.println("cur_dir: " + cur_dir); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path visitedFile,BasicFileAttributes fileAttributes) throws IOException { System.out.println("FILE NAME: "+ visitedFile.getFileName()); return FileVisitResult.CONTINUE; } }; FileSystem fileSystem = FileSystems.getDefault(); String path = System.getProperty("user.home")+"/UTP8dir"; Path rootPath = fileSystem.getPath(path); try { Files.walkFileTree(rootPath, simpleFileVisitor); } catch (IOException ioe) { ioe.printStackTrace(); }
问题分析
- 代码存在**重复嵌套定义
SimpleFileVisitor**的问题,外层与内部同名对象导致逻辑混乱; - Java匿名内部类访问外部变量时,要求变量为
final或等效final(Java 8+),直接修改普通String变量会触发编译错误,且String本身是不可变类型,无法直接在内部类中修改外部引用。
解决方案
一、解决外部作用域变量赋值问题
要让匿名内部类能修改外部变量,需使用可变容器类来封装变量,以下两种方式均可:
方式1:使用AtomicReference(线程安全容器)
import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.concurrent.atomic.AtomicReference; public class FileVisitorDemo { public static void main(String[] args) { // 用AtomicReference存储可变的当前目录路径 AtomicReference<String> currentDir = new AtomicReference<>(""); FileVisitor<Path> simpleFileVisitor = new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { String dirPath = dir.toFile().getPath(); currentDir.set(dirPath); // 修改外部容器的值 System.out.println("当前目录: " + currentDir.get()); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path visitedFile, BasicFileAttributes fileAttributes) throws IOException { System.out.println("文件名称: " + visitedFile.getFileName()); return FileVisitResult.CONTINUE; } }; FileSystem fileSystem = FileSystems.getDefault(); String path = System.getProperty("user.home") + "/UTP8dir"; Path rootPath = fileSystem.getPath(path); try { Files.walkFileTree(rootPath, simpleFileVisitor); } catch (IOException ioe) { ioe.printStackTrace(); } // 遍历结束后获取最后访问的目录 System.out.println("最后访问的目录: " + currentDir.get()); } }
方式2:自定义可变字符串类
// 自定义可变容器类 class MutableString { public String value = ""; } // 使用示例 import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; public class FileVisitorDemo { public static void main(String[] args) { MutableString currentDir = new MutableString(); FileVisitor<Path> simpleFileVisitor = new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { currentDir.value = dir.toFile().getPath(); System.out.println("当前目录: " + currentDir.value); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path visitedFile, BasicFileAttributes fileAttributes) throws IOException { System.out.println("文件名称: " + visitedFile.getFileName()); return FileVisitResult.CONTINUE; } }; FileSystem fileSystem = FileSystems.getDefault(); String path = System.getProperty("user.home") + "/UTP8dir"; Path rootPath = fileSystem.getPath(path); try { Files.walkFileTree(rootPath, simpleFileVisitor); } catch (IOException ioe) { ioe.printStackTrace(); } } }
二、实现收集所有.txt文件内容到String的最终目标
结合需求,使用StringBuilder(可变字符串容器)来汇总所有.txt文件内容,同时记录目录信息:
import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.concurrent.atomic.AtomicReference; public class TxtContentCollector { public static void main(String[] args) { AtomicReference<String> currentDir = new AtomicReference<>(""); // 用于存储所有txt文件内容的可变容器 StringBuilder allTxtContent = new StringBuilder(); FileVisitor<Path> txtFileVisitor = new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { currentDir.set(dir.toFile().getPath()); allTxtContent.append("=== 进入目录: ").append(currentDir.get()).append(" ===\n"); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { // 判断是否为txt文件 if (file.getFileName().toString().endsWith(".txt")) { allTxtContent.append("\n--- 文件: ").append(file.getFileName()).append(" ---\n"); // 读取文件内容并追加到容器中 String content = Files.readString(file, StandardCharsets.UTF_8); allTxtContent.append(content).append("\n"); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { // 处理文件访问失败的情况 System.err.println("访问文件失败: " + file); return FileVisitResult.CONTINUE; } }; FileSystem fileSystem = FileSystems.getDefault(); String path = System.getProperty("user.home") + "/UTP8dir"; Path rootPath = fileSystem.getPath(path); try { Files.walkFileTree(rootPath, txtFileVisitor); } catch (IOException ioe) { ioe.printStackTrace(); } // 输出汇总的所有内容 System.out.println("所有.txt文件内容汇总:\n"); System.out.println(allTxtContent.toString()); } }
内容的提问来源于stack exchange,提问作者Javadkhan
相关产品推荐
相关产品推荐

