Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Классы File и Path

Классы File и Path являются основными средствами для работы с файловой системой в Java. Они предоставляют методы для создания, удаления, получения информации о файлах и директориях, а также для навигации по файловой системе.

Класс File

File — это класс, который представляет файл или директорию в файловой системе. Он предоставляет методы для выполнения различных операций с файлами и директориями.

Основные методы класса File:

Создание объекта File:
File file = new File("example.txt");


Проверка существования файла:
boolean exists = file.exists();


Создание нового файла:
boolean created = file.createNewFile();


Удаление файла:
boolean deleted = file.delete();


Получение имени файла:
String name = file.getName();


Получение пути файла:
String path = file.getPath();


Получение абсолютного пути файла:
String absolutePath = file.getAbsolutePath();


Проверка, является ли объект директорией:
boolean isDirectory = file.isDirectory();


Список файлов в директории:
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f.getName());
}


Пример работы с классом File:
File file = new File("example.txt");

// Проверка существования файла
if (!file.exists()) {
// Создание нового файла
try {
boolean created = file.createNewFile();
if (created) {
System.out.println("File created successfully.");
}
} catch (IOException e) {
e.printStackTrace();
}
}

// Получение информации о файле
System.out.println("File name: " + file.getName());
System.out.println("File path: " + file.getPath());
System.out.println("Absolute path: " + file.getAbsolutePath());
System.out.println("Is directory: " + file.isDirectory());

// Удаление файла
boolean deleted = file.delete();
if (deleted) {
System.out.println("File deleted successfully.");
}


Класс Path

Path — это интерфейс в пакете java.nio.file, представляющий путь в файловой системе. Класс Paths предоставляет статические методы для создания экземпляров Path.

Основные методы класса Path:

Создание объекта Path:
Path path = Paths.get("example.txt");


Получение имени файла:
Path fileName = path.getFileName();


Получение родительского пути:
Path parent = path.getParent();


Получение корневого элемента пути:
Path root = path.getRoot();


Проверка существования файла:
boolean exists = Files.exists(path);


Создание нового файла:

Files.createFile(path);


Удаление файла:
Files.delete(path);


Получение информации о файле:
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);


Пример работы с классом Path:

Path path = Paths.get("example.txt");

// Проверка существования файла
if (!Files.exists(path)) {
// Создание нового файла
try {
Path createdFilePath = Files.createFile(path);
System.out.println("File created at path: " + createdFilePath);
} catch (IOException e) {
e.printStackTrace();
}
}

// Получение информации о файле
System.out.println("File name: " + path.getFileName());
System.out.println("Parent path: " + path.getParent());
System.out.println("Root path: " + path.getRoot());

// Удаление файла
try {
Files.delete(path);
System.out.println("File deleted successfully.");
} catch (IOException e) {
e.printStackTrace();
}


Сравнение File и Path

Структура данных:

File представляет файл или директорию как объект.
Path представляет путь к файлу или директории.

Функциональность:

File предоставляет методы для работы с файлами и директориями.
Path предоставляет более гибкие и мощные методы для работы с путями в файловой системе через пакеты java.nio.file.

Совместимость:

File используется в старом подходе к работе с I/O.
Path и связанные с ним классы (Files, Paths) являются частью нового подхода, введенного в Java 7, и предоставляют больше возможностей и гибкости.

#Java #Training #IO #File #Path
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

Глава 2. Современный NIO.2 (java.nio.file)

Интерфейс Path – путь к файлу/директории

Интерфейс Path — это ключевая абстракция в Java NIO.2 (New I/O 2), представленная в Java 7 (2011 год, пакет java.nio.file). Он заменил устаревший класс java.io.File и предоставляет платформонезависимый способ работы с путями к файлам и директориям. В отличие от File, который напрямую обращался к файловой системе ОС, Path — это чистая абстракция: объект Path может существовать в памяти JVM, даже если соответствующего файла на диске нет.


Что такое Path

Path (от англ. "путь") — это интерфейс, представляющий иерархическое расположение файла или директории в файловой системе. Главное отличие от старого File в том, что Path — это не файл, а именно путь. Он не знает, существует ли файл, доступен ли он для чтения, и т.д. — эти вопросы решаются через другие классы NIO.2, например Files.

Абстракция — в программировании это способ представления сложной реальности в упрощённом виде, скрывая детали реализации. Path абстрагирует различия между файловыми системами: в Windows путь разделяется обратным слешем C:\Users\file.txt, в Linux и macOS — прямым /home/user/file.txt. Path нормализует эти различия на уровне API.


Создание объекта Path

Основной способ создания — статический метод фабрики Paths.get(). Класс Paths (во множественном числе) — это утилитарный класс-фабрика, который делегирует создание Path текущей файловой системе по умолчанию.

Способ 1: единая строка пути
import java.nio.file.Path;
import java.nio.file.Paths;

// Linux / macOS
Path linuxPath = Paths.get("/home/user/documents/report.txt");

// Windows
Path windowsPath = Paths.get("C:\\Users\\Admin\\file.txt");

При передаче строки с обратными слешами в Windows Paths.get() корректно парсит путь. Важно: в Java-строке обратный слеш — это escape-символ, поэтому в литерале пишется \\, но при чтении из внешнего источника (конфиг, ввод пользователя) это не требуется.

Способ 2: сегменты пути (varargs)
Path path = Paths.get("/home", "user", "documents", "report.txt");
// Результат: /home/user/documents/report.txt

Этот способ удобен при динамической сборке пути из частей. Метод get(String first, String... more) принимает первый сегмент и переменное число дополнительных сегментов (varargs — механизм Java для передачи произвольного количества аргументов одного типа). Сегменты автоматически соединяются разделителем, специфичным для ОС.

Способ 3: относительный путь
Path relative = Paths.get("src", "main", "java", "App.java");
// Результат: src/main/java/App.java (на Unix) или src\main\java\App.java (на Windows)

Относительный путь строится относительно текущей рабочей директории JVM — это директория, из которой был запущен процесс Java.

Способ 4: через FileSystem
import java.nio.file.FileSystems;

Path path = FileSystems.getDefault().getPath("/tmp", "data.txt");

FileSystems.getDefault() возвращает объект FileSystem — абстракцию над конкретной файловой системой. Этот подход используется при работе с нестандартными файловыми системами (например, ZIP-архив как файловая система через FileSystems.newFileSystem()).


Ключевые методы интерфейса Path

toString() — строковое представление
Path p = Paths.get("/home/user/file.txt");
System.out.println(p.toString());
// Вывод: /home/user/file.txt

Метод возвращает строку пути в нативном формате ОС. Это не просто геттер — он выполняет конкатенацию сегментов с правильным разделителем.

getFileName() — имя файла или последняя директория
Path p = Paths.get("/home/user/documents/report.txt");
Path fileName = p.getFileName(); // report.txt

Path dir = Paths.get("/home/user/documents");
Path dirName = dir.getFileName(); // documents

Возвращает последний сегмент пути как новый объект Path. Если путь — корень (/), возвращает null.

getParent() — родительская директория
Path p = Paths.get("/home/user/documents/report.txt");
Path parent = p.getParent(); // /home/user/documents

Path root = Paths.get("/");
Path rootParent = root.getParent(); // null

Возвращает путь без последнего сегмента. Если у пути нет родителя (корень или относительный путь из одного сегмента), возвращает null.

getRoot() — корневая часть пути
Path absolute = Paths.get("/home/user/file.txt");
Path root = absolute.getRoot(); // /

Path relative = Paths.get("src/main/java");
Path relRoot = relative.getRoot(); // null

Для абсолютного пути возвращает корневой элемент (/ на Unix, C:\ на Windows). Для относительного — null, так как корень не определён.

getNameCount() — количество сегментов

Path p = Paths.get("/home/user/documents/report.txt");
int count = p.getNameCount(); // 4 (home, user, documents, report.txt)

Возвращает число имен (сегментов) в пути, исключая корень. Корень не считается именем — это отправная точка иерархии.

getName(int index) — сегмент по индексу
Path p = Paths.get("/home/user/documents/report.txt");
Path segment = p.getName(1); // user

Индексация начинается с 0. Индекс 0 — первый сегмент после корня. Выход за границы выбрасывает IllegalArgumentException.

subpath(int beginIndex, int endIndex) — подпуть
Path p = Paths.get("/home/user/documents/report.txt");
Path sub = p.subpath(1, 3); // user/documents

Возвращает подпуть от beginIndex (включительно) до endIndex (исключительно). Индексы отсчитываются от корня, но сам корень в результат не включается.

isAbsolute() — проверка на абсолютность
Path abs = Paths.get("/etc/passwd");
boolean isAbs = abs.isAbsolute(); // true

Path rel = Paths.get("config/app.properties");
boolean isRel = rel.isAbsolute(); // false

Абсолютный путь содержит корневой элемент и однозначно указывает местоположение в файловой системе. Относительный — зависит от текущей директории.

toAbsolutePath() — преобразование в абсолютный
Path rel = Paths.get("config/app.properties");
Path abs = rel.toAbsolutePath();
// Результат: /current/working/dir/config/app.properties

Если путь уже абсолютный — возвращает себя. Иначе резолвит относительно текущей рабочей директории.

normalize() — нормализация пути
Path p = Paths.get("/home/user/../documents/./report.txt");
Path norm = p.normalize(); // /home/documents/report.txt

Удаляет избыточные сегменты: . (текущая директория) и .. (родительская директория). Не проверяет реальное существование файловой системы — работает чисто на уровне строк.

resolve(Path other) — резолюция пути
Path base = Paths.get("/home/user");
Path resolved = base.resolve("documents/report.txt");
// Результат: /home/user/documents/report.txt

Если other абсолютный — возвращает other. Если относительный — присоединяет к base. Эквивалент операции "base/other".

relativize(Path other) — относительный путь между двумя путями
Path base = Paths.get("/home/user");
Path target = Paths.get("/home/user/documents/report.txt");
Path rel = base.relativize(target); // documents/report.txt

Вычисляет, как добраться от base до target относительным путём. Оба пути должны быть либо абсолютными, либо относительными.

startsWith(Path other) / endsWith(Path other)
Path p = Paths.get("/home/user/documents");
boolean starts = p.startsWith(Paths.get("/home")); // true
boolean ends = p.endsWith(Paths.get("documents")); // true

Сравнивают сегменты пути, а не строки. startsWith проверяет, начинается ли путь с указанных сегментов с учётом иерархии.


#Java #для_новичков #beginner #IO #NIO #Path
👍4
toFile() — обратная совместимость
Path path = Paths.get("/tmp/data.txt");
java.io.File legacyFile = path.toFile();

Преобразует Path в старый java.io.File. Полезно при интеграции с legacy-кодом или библиотеками, которые ещё не перешли на NIO.2.

iterator() — итерация по сегментам
Path p = Paths.get("/home/user/file.txt");
for (Path segment : p) {
System.out.println(segment);
}
// Вывод: home, user, file.txt

Path реализует Iterable<Path>, что позволяет использовать for-each для обхода сегментов. Корень в итерацию не включается.

compareTo(Path other) — лексикографическое сравнение
Path p1 = Paths.get("/a/b/c");
Path p2 = Paths.get("/a/b/d");
int result = p1.compareTo(p2); // отрицательное число (c < d)

Сравнивает пути посегментно, учитывая регистр (на системах с case-sensitive файловыми системами). Реализует Comparable<Path>, что позволяет сортировать пути.


Путь байтов в памяти JVM при работе с Path

Когда вы пишете Paths.get("/home/user/file.txt"), в памяти JVM происходит следующее:

1. Создание строки в пуле строк (String Pool)
Литерал "/home/user/file.txt" — это объект String.

При компиляции он попадает в пул строк (String Pool) — специальная область в Metaspace (начиная с Java 8; ранее — PermGen).
Пул строк — это кэш строковых литералов, где одинаковые строки представлены единственным экземпляром. Это экономит память, так как строки в Java иммутабельны (неизменяемы).
Metaspace — область памяти JVM, выделенная под метаданные классов, константы, пул строк и другие внутренние структуры.

В отличие от старого PermGen, Metaspace использует нативную память (native memory) вне кучи и расширяется динамически.

2. Вызов Paths.get() и создание объекта Path
Метод Paths.get() вызывает FileSystems.getDefault().getPath(first, more).
Файловая система по умолчанию (обычно UnixFileSystem на Linux/macOS или WindowsFileSystem на Windows) создаёт конкретную реализацию Path — например, UnixPath или WindowsPath.
Эти объекты создаются в куче (Heap) — основной области памяти для объектов Java.

Объект Path хранит:
Ссылку на файловую систему (FileSystem)
Массив байтов или строк, представляющих сегменты пути
Хэш-код для кэширования

3. Работа GC с объектами Path
Объекты Path — это обычные Java-объекты в куче.
GC (Garbage Collector, сборщик мусора) — это механизм JVM, автоматически освобождающий память от объектов, на которые больше нет ссылок.
Когда переменная Path выходит из области видимости (например, метод завершился, и локальная переменная недоступна), объект становится кандидатом на сборку мусора.
Однако сама строка-путь в пуле строк остаётся — пул строк не подлежит обычной сборке мусора, так как JVM предполагает, что строковые литералы могут понадобиться снова.

Если Path создаётся динамически (например, Paths.get(base, userInput)), и входные данные уникальны при каждом вызове, строки не попадают в пул автоматически (если только не вызван intern()). Такие строки создаются в куче и полностью управляются GC. При частом создании временных путей (например, в цикле обработки 10 000 файлов) объекты Path и связанные строки накапливаются в Young Generation (молодое поколение) кучи.

Young Generation — часть кучи, где размещаются новые объекты. Она делится на Eden (пространство, куда попадают все новые объекты) и два Survivor-пространства (S0, S1). Когда Eden заполняется, запускается Minor GC — быстрая сборка мусора, перемещающая живые объекты в Survivor. Объекты Path, созданные внутри короткоживущего метода, обычно уничтожаются на этом этапе.
Если объект Path кэшируется (например, сохраняется в статическое поле или долгоживущую коллекцию), он переходит в Old Generation (старое поколение, Tenured). Там он живёт до Major GC (или Full GC) — более тяжёлой сборки, которая затрагивает всю кучу. Major GC запускается, когда Old Generation заполняется.

Важно: Path — лёгкий объект. Он не держит открытых файловых дескрипторов (в отличие от FileInputStream). Поэтому его сборка мусора безопасна в любой момент. Однако если Path используется для открытия ресурса (через Files.newInputStream(path)), то сам поток уже требует явного закрытия через try-with-resources, иначе дескриптор останется открытым на уровне ОС, даже если объект Path собран GC.

4. Байты на уровне ОС
Когда Path передаётся в метод Files.readAllBytes(path), JVM через JNI (Java Native Interface — механизм вызова нативного кода) обращается к системному вызову ОС. Путь преобразуется в массив байтов в кодировке, понятной файловой системе (обычно UTF-8). Эти байты передаются в системный вызов open() (POSIX) или CreateFile() (Windows). Прочитанные данные возвращаются в JVM как массив байтов в куче (heap-allocated byte array).

#Java #для_новичков #beginner #IO #NIO #Path
👍5
Интеграции и практические примеры

Пример 1: Обход директории с фильтрацией
import java.nio.file.*;
import java.io.IOException;

public class DirectoryScanner {
public static void scanDirectory(Path root) throws IOException {
// Files.walk — рекурсивный обход дерева директорий
try (Stream<Path> paths = Files.walk(root, 3)) { // глубина 3 уровня
paths.filter(Files::isRegularFile) // только файлы
.filter(p -> p.toString().endsWith(".java"))
.forEach(p -> System.out.println(p.toAbsolutePath()));
}
}
}

Files.walk() возвращает Stream<Path> — ленивый поток (lazy stream), который читает директории по мере необходимости. Это предотвращает загрузку всех путей в память сразу, что критично для больших файловых деревьев.

Пример 2: Резолюция путей в конфигурации приложения
public class ConfigResolver {
private final Path baseDir;

public ConfigResolver(String basePath) {
this.baseDir = Paths.get(basePath).toAbsolutePath().normalize();
}

public Path resolveConfig(String configName) {
// resolve объединяет baseDir с относительным путём
Path resolved = baseDir.resolve("config").resolve(configName);

// безопасность: проверяем, что результирующий путь всё ещё внутри baseDir
if (!resolved.startsWith(baseDir)) {
throw new SecurityException("Path traversal attempt detected");
}
return resolved;
}
}

Этот паттерн защищает от Path Traversal (атака обхода пути), когда злоумышленник передаёт ../../../etc/passwd в качестве configName. Метод startsWith() гарантирует, что результирующий путь остаётся внутри разрешённой директории.

Пример 3: Копирование с проверкой
public void safeCopy(Path source, Path target) throws IOException {
// normalize убирает . и .. перед проверкой
Path normalizedSource = source.toAbsolutePath().normalize();
Path normalizedTarget = target.toAbsolutePath().normalize();

// getParent возвращает директорию назначения
Path targetDir = normalizedTarget.getParent();
if (targetDir != null && !Files.exists(targetDir)) {
Files.createDirectories(targetDir); // создаём всю цепочку директорий
}

// StandardCopyOption — перечисление, определяющее поведение копирования
Files.copy(normalizedSource, normalizedTarget,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES);
}

StandardCopyOption — это enum (перечисление), тип данных в Java, представляющий фиксированный набор констант. REPLACE_EXISTING разрешает перезапись существующего файла, COPY_ATTRIBUTES копирует метаданные (права доступа, временные метки).

Пример 4: Работа с временными файлами
public Path createTempFileWithPath() throws IOException {
// getDefault() возвращает файловую систему по умолчанию
Path tempDir = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"));

// createTempFile создаёт файл с префиксом и суффиксом
Path tempFile = Files.createTempFile(tempDir, "app_", ".tmp");

System.out.println("Created: " + tempFile.toAbsolutePath());
return tempFile;
}

System.getProperty("java.io.tmpdir") возвращает путь к временной директории ОС. Files.createTempFile гарантирует уникальность имени через атомарную операцию на уровне файловой системы.


Path vs File: ключевые различия

Класс java.io.File (существует с Java 1.0) имеет ряд архитектурных недостатков:
Смешение ответственности: File одновременно представляет и путь, и файл, и директорию. Path разделяет эти концепции.
Ограниченная поддержка символических ссылок: File не различает символическую ссылку и целевой файл. Path в сочетании с Files предоставляет полный контроль.
Отсутствие нормализации: File не предоставляет методов для работы с . и ... Path.normalize() решает это.
Нет поддержки файловых систем, отличных от дефолтной: Path работает с любой FileSystem — ZIP, память, сетевые ФС.
Методы File возвращают boolean вместо исключений: file.delete() возвращает false при ошибке, не объясняя причину. Files.delete(path) выбрасывает IOException с деталями.


#Java #для_новичков #beginner #IO #NIO #Path
👍5