Доступ к файловой системе
Для нового кода основной API — Path и Files. File остается для старых интерфейсов.
Path — описание пути
Создание Path не обращается к диску:
Path base = Path.of("data");
Path report = base.resolve("reports").resolve("daily.txt");
report.getFileName(); // daily.txt
report.getParent(); // data/reports
report.isAbsolute(); // falsenormalize() синтаксически убирает . и .., но не разрешает symlink:
Path normalized = Path.of("a", "..", "b").normalize(); // b
Path real = existing.toRealPath(); // I/O, существование, symlinktoAbsolutePath() не делает путь каноническим и не проверяет существование.
Чтение и запись
String text = Files.readString(path, StandardCharsets.UTF_8);
Files.writeString(
path,
text,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING
);readString/readAllBytes требуют, чтобы содержимое помещалось в память. Для больших файлов — stream/reader.
Создание, копирование, перемещение
Files.createDirectories(report.getParent());
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);ATOMIC_MOVE поддерживается не каждой файловой системой и может бросить AtomicMoveNotSupportedException. При этой опции остальные options игнорируются, а поведение при уже существующем target зависит от реализации. Нельзя считать сочетание ATOMIC_MOVE + REPLACE_EXISTING переносимой гарантией атомарной замены. Перемещение между разными file stores обычно не атомарно.
Files.createFile(path) атомарно проверяет отсутствие и создает файл. Проверка exists() затем создание — race condition.
Обход директории
try (Stream<Path> entries = Files.list(directory)) {
entries.filter(Files::isRegularFile)
.forEach(System.out::println);
}Stream закрывают: он держит системный ресурс. Files.walk() обходит глубже и тоже требует try-with-resources.
Рекурсивное удаление
Удалять нужно детей до родителя:
Files.walkFileTree(validatedRoot, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(
Path file, BasicFileAttributes attrs
) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(
Path dir, IOException error
) throws IOException {
if (error != null) throw error;
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});Перед такой операцией корень разрешают и валидируют явно. Нельзя принимать непроверенный /, домашний каталог или путь после подстановки. По умолчанию walkFileTree не следует по symlink; включение FOLLOW_LINKS меняет модель угроз.
File и Path
File | Path + Files |
|---|---|
многие ошибки превращает в false | сообщает причину через exception |
| нет современного copy API | copy/move/attributes/walk |
| класс | Path — interface |
file.toPath() | path.toFile() для совместимости |
При проверке, лежит ли пользовательский путь внутри разрешенного каталога, простой normalize().startsWith(base) не защищает от symlink. Для существующих объектов сравнивают реальные пути и учитывают момент проверки/использования.
На собеседовании
- Создает ли Path файл? Нет.
- normalize и toRealPath? Синтаксическое упрощение против обращения к FS и разрешения symlink.
- Почему
exists+createгоняется? Состояние меняется между вызовами; нуженcreateFile. - Нужно ли закрывать
Files.list/walk? Да. - Всегда ли ATOMIC_MOVE работает? Нет.
- Чем Path лучше File? Полный NIO API и информативные исключения.