NIO.2 в Java: работа с файлами через Path и Files - Вопросы
Всего: 5 вопросов
1. Что такое Path и Files в NIO.2 и как создать объект Path?
Что такое Path и Files в NIO.2 и как создать объект Path?
NIO.2 — API пакета java.nio.file (Java 7). Интерфейс Path описывает путь к файлу или каталогу как последовательность элементов-имён, а утилитный класс Files со статическими методами выполняет операции над файлами: проверяет, читает, пишет, создаёт, копирует, перемещает, удаляет, обходит каталоги. В отличие от java.io.File, методы Files при неудаче бросают исключение с причиной (NoSuchFileException, FileAlreadyExistsException, DirectoryNotEmptyException), а не возвращают false.
- Path.of(first, more...) (Java 11) — рекомендуемый способ:
Path.of("data", "reports", "2026.txt"). - Paths.get(...) (Java 7) — тот же результат, нужен в проектах на Java 7–10; документация предупреждает, что класс
Pathsможет стать устаревшим. - Path.of(URI) — из URI со схемой
file:; дляjar:без открытой файловой системы —FileSystemNotFoundException. - file.toPath() — переход от старого
File, обратно —path.toFile().
Создание Path к диску не обращается: файл может не существовать, проверяется только синтаксис (недопустимый путь даёт InvalidPathException). Части пути возвращают getFileName(), getParent(), getRoot() — все они возвращают Path и могут вернуть null (у относительного пути нет корня).
2. Как проверить, что два Path указывают на один и тот же файл? Чем отличаются equals(), Files.isSameFile(), toAbsolutePath() и toRealPath()?
Как проверить, что два Path указывают на один и тот же файл? Чем отличаются equals(), Files.isSameFile(), toAbsolutePath() и toRealPath()?
Надёжно — через Files.isSameFile(p1, p2) или сравнение toRealPath(): оба обращаются к файловой системе. equals(), startsWith() и endsWith() у Path сравнивают пути как последовательности элементов, не читая диск, поэтому a/../b.txt и b.txt для них разные пути.
- equals() — посимвольно по элементам; в Windows без учёта регистра (
B.TXTравноb.txt), в Linux и macOS с учётом. Путь как ключHashMapведёт себя по-разному на разных ОС. - normalize() — убирает
.и..только текстово:p1.normalize().equals(p2)ужеtrue, но символические ссылки не раскрываются. - toAbsolutePath() — приписывает рабочий каталог, к диску не обращается, работает для несуществующих файлов и оставляет
..как есть. - toRealPath() — канонический путь без
.., с раскрытыми ссылками; для отсутствующего файла бросаетNoSuchFileException. - Files.isSameFile() — читает файловую систему и отвечает, один ли это файл.
Если файла ещё нет, а нужен абсолютный путь без .., используют p.toAbsolutePath().normalize(). Та же пара normalize() + startsWith(base) защищает от выхода за пределы каталога (path traversal), когда имя файла приходит от пользователя.
3. Как прочитать и записать текстовый файл через Files? Что выбрать для большого файла и какая кодировка используется по умолчанию?
Как прочитать и записать текстовый файл через Files? Что выбрать для большого файла и какая кодировка используется по умолчанию?
Небольшой файл читают целиком: Files.readString(path) (Java 11) — одной строкой, Files.readAllLines(path) — списком строк, Files.readAllBytes(path) — массивом байтов. Пишут через Files.writeString(path, text) или Files.write(path, lines); без опций файл создаётся или перезаписывается, а Files.write(path, lines) добавляет после каждой строки разделитель строк ОС. Файл открывается и закрывается внутри метода.
- Большой файл:
readString()иreadAllLines()грузят всё в память (лог на гигабайты —OutOfMemoryError). Читают построчно:Files.lines(path)(Java 8, ленивыйStream<String>) илиFiles.newBufferedReader(path); пишут черезFiles.newBufferedWriter(path). Все три объекта закрывают в try-with-resources. - Кодировка: все текстовые методы
Filesбез параметраCharsetиспользуют UTF-8 на любой ОС и в любой версии Java (в отличие отFileReaderдо Java 17). - Файл в другой кодировке:
readString()иreadAllLines()не выдают «кракозябры», а бросаютMalformedInputException(например,Input length = 1для windows-1251); уFiles.lines()ошибка всплывает при обходе потока в видеUncheckedIOException. Решение — передать настоящую кодировку:Files.readString(p, Charset.forName("windows-1251")).
4. Какие методы Files создают, копируют, перемещают и удаляют файлы и какие исключения они бросают?
Какие методы Files создают, копируют, перемещают и удаляют файлы и какие исключения они бросают?
Каждый метод при неудаче бросает конкретное исключение, а не возвращает false:
- createFile(), createDirectory() — объект уже есть:
FileAlreadyExistsException; нет родительского каталога:NoSuchFileException. - createDirectories() — создаёт все недостающие родительские каталоги; если каталог уже есть, это не ошибка.
- copy(src, target) — если цель существует, без
StandardCopyOption.REPLACE_EXISTINGбудетFileAlreadyExistsException. Каталог копируется пустым, без содержимого.COPY_ATTRIBUTESпереносит время изменения и права. - move(src, target) — переименовывает или перемещает; цель есть —
FileAlreadyExistsException(безREPLACE_EXISTING);ATOMIC_MOVEвыполняет перемещение одной операцией ОС, между разными дисками —AtomicMoveNotSupportedException. - delete(path) — удаляет файл или пустой каталог; файла нет —
NoSuchFileException, каталог не пуст —DirectoryNotEmptyException. - deleteIfExists(path) — возвращает
false, если файла не было; для непустого каталога тоже бросаетDirectoryNotEmptyException.
Проверка Files.exists() перед операцией не страхует: между проверкой и действием файл может изменить другая программа (TOCTOU). Надёжнее сразу выполнять операцию и обрабатывать исключение. Временный файл Files.createTempFile(prefix, suffix) сам не удаляется — удаляйте его в finally или открывайте с DELETE_ON_CLOSE.
5. Чем отличаются Files.list(), Files.walk(), Files.find() и Files.newDirectoryStream()? Как удалить каталог со всем содержимым?
Чем отличаются Files.list(), Files.walk(), Files.find() и Files.newDirectoryStream()? Как удалить каталог со всем содержимым?
- Files.list(dir) — только непосредственные элементы каталога (сам
dirне входит),Stream<Path>, порядок не гарантирован; для файла вместо каталога —NotDirectoryException. - Files.walk(dir[, maxDepth]) — всё дерево рекурсивно; первым идёт сам
dir, каталог всегда раньше своего содержимого. - Files.find(dir, maxDepth, (path, attrs) -> ...) — обход с фильтром, которому уже переданы атрибуты, поэтому не нужен лишний вызов
Files.isRegularFile(). - Files.newDirectoryStream(dir, "*.{java,kt}") — один уровень с фильтром по glob-шаблону, возвращает
DirectoryStream<Path>для цикла for-each.
Все четыре держат каталог открытым, их закрывают через try-with-resources. По символическим ссылкам walk() и find() не переходят без FileVisitOption.FOLLOW_LINKS. Расширение в фильтре проверяют строкой: p.getFileName().toString().endsWith(".txt") — p.endsWith(".txt") не найдёт ни одного файла.
Готового метода для удаления непустого каталога нет: Files.delete() удаляет только пустой. Обходят дерево через Files.walk(root), сортируют sorted(Comparator.reverseOrder()), чтобы содержимое шло раньше своего каталога, и удаляют по одному (IOException в лямбде заворачивают в UncheckedIOException). Без reverseOrder() первым идёт сам корень, и сразу будет DirectoryNotEmptyException. Для очень больших деревьев используют Files.walkFileTree() с удалением каталога в postVisitDirectory().