«1. Обзор

В этой статье мы узнаем, как использовать новый I/O (NIO2) Path API в Java.

API-интерфейсы Path в NIO2 представляют собой одну из основных новых функциональных областей, поставляемых с Java 7, и, в частности, подмножество API-интерфейсов новой файловой системы наряду с API-интерфейсами файлов.

2. Настройка

Поддержка NIO2 включена в пакет java.nio.file. Таким образом, настройка вашего проекта для использования Path API — это просто вопрос импорта всего из этого пакета:

import java.nio.file.*;

Так как примеры кода в этой статье, вероятно, будут выполняться в разных средах, давайте разберемся с домашним каталогом. пользователя:

private static String HOME = System.getProperty("user.home");

Эта переменная будет указывать на допустимое местоположение в любой среде.

Класс Paths является основной точкой входа для всех операций с путями в файловой системе. Это позволяет нам создавать и управлять путями к файлам и каталогам.

Следует отметить, что операции с путями в основном носят синтаксический характер; они не влияют на базовую файловую систему, и файловая система также не влияет на их успешность или неудачу. Это означает, что передача несуществующего пути в качестве параметра операции пути не влияет на ее успешность или неудачу.

3. Операции с путями

В этом разделе мы представим основной синтаксис, используемый в операциях с путями. Как следует из названия, класс Path является программным представлением пути в файловой системе.

Объект Path содержит имя файла и список каталогов, используемых для создания пути, и используется для проверки, поиска и управления файлами.

Вспомогательный класс java.nio.file.Paths (во множественном числе) является формальным способом создания объектов Path. У него есть два статических метода для создания пути из строки пути:

Path path = Paths.get("path string");

Независимо от того, используем ли мы прямую или обратную косую черту в строке пути, API разрешает этот параметр в соответствии с требованиями базовой файловой системы.

И из объекта java.net.URI:

Path path = Paths.get(URI object);

Теперь мы можем продолжить и увидеть их в действии.

4. Создание пути

Чтобы создать объект пути из строки пути:

@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles/baeldung");
 
    assertEquals("\\articles\\baeldung", p.toString());
}

API get может принимать переменный аргумент аргументов частей строки пути (в данном случае статьи и баелдунг) дополнение к первой части (в данном случае статьи).

Если мы предоставим эти части вместо полной строки пути, они будут использоваться для создания объекта Path, нам не нужно включать разделители имен (косые черты) в часть переменных аргументов:

@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles", "baeldung");
    
    assertEquals("\\articles\\baeldung", p.toString());
}

5. Получение информации о пути

Вы можете думать об объекте пути как об элементах имени как о последовательности. Строка пути, такая как E:\\baeldung\\articles\\java, состоит из трех элементов имени, т. е. baeldung, article и java. Самый высокий элемент в структуре каталогов будет располагаться по индексу 0, в данном случае это baeldung.

Самый нижний элемент в структуре каталогов будет располагаться по индексу [n-1], где n — количество элементов имени в пути. Этот самый нижний элемент называется именем файла, независимо от того, является ли он фактическим файлом или нет:

@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
    Path p = Paths.get("/articles/baeldung/logs");

    Path fileName = p.getFileName();
 
    assertEquals("logs", fileName.toString());
}

Доступны методы для извлечения отдельных элементов по индексу:

@Test
public void givenPath_whenRetrievesNameByIndex_thenCorrect() {
    Path p = Paths.get("/articles/baeldung/logs");
    Path name0 = getName(0);
    Path name1 = getName(1);
    Path name2 = getName(2);
    assertEquals("articles", name0.toString());
    assertEquals("baeldung", name1.toString());
    assertEquals("logs", name2.toString());
}

или части пути с помощью эти диапазоны индексов:

@Test
public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() {
    Path p = Paths.get("/articles/baeldung/logs");

    Path subPath1 = p.subpath(0,1);
    Path subPath2 = p.subpath(0,2);
 
    assertEquals("articles", subPath1.toString());
    assertEquals("articles\\baeldung", subPath2.toString());
    assertEquals("articles\\baeldung\\logs", p.subpath(0, 3).toString());
    assertEquals("baeldung", p.subpath(1, 2).toString());
    assertEquals("baeldung\\logs", p.subpath(1, 3).toString());
    assertEquals("logs", p.subpath(2, 3).toString());
}

Каждый путь связан с родительским путем или нулевым, если у пути нет родителя. Родительский объект пути состоит из корневого компонента пути, если он есть, и каждого элемента пути, кроме имени файла. Например, родительский путь /a/b/c равен /a/b, а путь /a равен null:

@Test
public void givenPath_whenRetrievesParent_thenCorrect() {
    Path p1 = Paths.get("/articles/baeldung/logs");
    Path p2 = Paths.get("/articles/baeldung");
    Path p3 = Paths.get("/articles");
    Path p4 = Paths.get("/");

    Path parent1 = p1.getParent();
    Path parent2 = p2.getParent();
    Path parent3 = p3.getParent();
    Path parent4 = p4.getParenth();

    assertEquals("\\articles\\baeldung", parent1.toString());
    assertEquals("\\articles", parent2.toString());
    assertEquals("\\", parent3.toString());
    assertEquals(null, parent4);
}

Мы также можем получить корневой элемент пути:

@Test
public void givenPath_whenRetrievesRoot_thenCorrect() {
    Path p1 = Paths.get("/articles/baeldung/logs");
    Path p2 = Paths.get("c:/articles/baeldung/logs");

    Path root1 = p1.getRoot();
    Path root2 = p2.getRoot();

    assertEquals("\\", root1.toString());
    assertEquals("c:\\", root2.toString());
}

6. Нормализация пути

Многие файловые системы используют нотацию «.» для обозначения текущего каталога и «..» для обозначения родительского каталога. Может возникнуть ситуация, когда путь содержит избыточную информацию о каталоге.

Например, рассмотрим следующие строки пути:

/baeldung/./articles
/baeldung/authors/../articles
/baeldung/articles

Все они разрешаются в одно и то же место /baeldung/articles. Первые два имеют избыточность, а последний — нет.

Нормализация пути включает удаление из него избыточности. Для этой цели предусмотрена операция Path.normalize().

«Этот пример теперь должен быть понятен:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
    Path p = Paths.get("/home/./baeldung/articles");

    Path cleanPath = p.normalize();
 
    assertEquals("\\home\\baeldung\\articles", cleanPath.toString());
}

Этот тоже:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
    Path p = Paths.get("/home/baeldung/../articles");

    Path cleanPath = p.normalize();
 
    assertEquals("\\home\\articles", cleanPath.toString());
}

7. Преобразование пути

Существуют операции для преобразования пути в выбранный формат представления. Чтобы преобразовать любой путь в строку, которую можно открыть из браузера, мы используем метод toUri:

@Test
public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
    Path p = Paths.get("/home/baeldung/articles.html");

    URI uri = p.toUri();
    assertEquals(
      "file:///E:/home/baeldung/articles.html", 
        uri.toString());
}

Мы также можем преобразовать путь в его абсолютное представление. Метод toAbsolutePath разрешает путь к каталогу файловой системы по умолчанию:

@Test
public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
    Path p = Paths.get("/home/baeldung/articles.html");

    Path absPath = p.toAbsolutePath();
 
    assertEquals(
      "E:\\home\\baeldung\\articles.html", 
        absPath.toString());
}

Однако, когда обнаруживается, что путь, который нужно разрешить, уже является абсолютным, метод возвращает его как есть:

@Test
public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
    Path p = Paths.get("E:\\home\\baeldung\\articles.html");

    Path absPath = p.toAbsolutePath();
 
    assertEquals(
      "E:\\home\\baeldung\\articles.html", 
        absPath.toString());
}

Мы также можем преобразовать любой путь в его реальный эквивалент, вызвав метод toRealPath. Этот метод пытается разрешить путь, сопоставляя его элементы с реальными каталогами и файлами в файловой системе.

Время использовать переменную, которую мы создали в разделе «Настройка», которая указывает на домашнюю позицию вошедшего в систему пользователя в файловой системе:

@Test
public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() {
    Path p = Paths.get(HOME);

    Path realPath = p.toRealPath();
 
    assertEquals(HOME, realPath.toString());
}

Приведенный выше тест на самом деле мало что говорит нам о поведении этой операции. Наиболее очевидным результатом является то, что если путь не существует в файловой системе, то операция выдаст исключение IOException, читайте дальше.

За неимением лучшего способа довести эту точку до ума, просто взгляните на следующий тест, который пытается преобразовать несуществующий путь в реальный путь:

@Test(expected = NoSuchFileException.class)
public void givenInExistentPath_whenFailsToConvert_thenCorrect() {
    Path p = Paths.get("E:\\home\\baeldung\\articles.html");
    
    p.toRealPath();
}

Тест завершается успешно, когда мы поймаем IOException. Фактическим подклассом IOException, который выдает эта операция, является NoSuchFileException.

8. Соединение путей

Соединение любых двух путей может быть достигнуто с помощью метода разрешения.

Проще говоря, мы можем вызвать метод разрешения для любого пути и передать частичный путь в качестве аргумента. Этот неполный путь добавляется к исходному пути:

@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
    Path p = Paths.get("/baeldung/articles");

    Path p2 = p.resolve("java");
 
    assertEquals("\\baeldung\\articles\\java", p2.toString());
}

Однако, когда строка пути, переданная методу разрешения, не является частичным путем; особенно абсолютный путь, затем возвращается переданный путь:

@Test
public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() {
    Path p = Paths.get("/baeldung/articles");

    Path p2 = p.resolve("C:\\baeldung\\articles\java");
 
    assertEquals("C:\\baeldung\\articles\\java", p2.toString());
}

То же самое происходит с любым путем, имеющим корневой элемент. Строка пути «java» не имеет корневого элемента, а строка пути «/java» имеет корневой элемент. Поэтому, когда вы передаете путь с корневым элементом, он возвращается как есть:

@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
    Path p = Paths.get("/baeldung/articles");

    Path p2 = p.resolve("/java");
 
    assertEquals("\\java", p2.toString());
}

9. Релятивизация путей

Термин релятивизация просто означает создание прямого пути между двумя известными путями. Например, если у нас есть каталог /baeldung и внутри него, у нас есть два других каталога, так что /baeldung/authors и /baeldung/articles являются допустимыми путями.

Путь к статьям относительно авторов будет описан как «переместиться на один уровень вверх в иерархии каталогов, затем в каталог статей» или ..\\articles:

@Test
public void givenSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("articles");
    Path p2 = Paths.get("authors");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);
 
    assertEquals("..\\authors", p1_rel_p2.toString());
    assertEquals("..\\articles", p2_rel_p1.toString());
}

Предположим, что мы перемещаем каталог статей в папку авторов чтобы они больше не были братьями и сестрами. Следующие релятивизирующие операции включают создание пути между baeldung и article и наоборот:

@Test
public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("/baeldung");
    Path p2 = Paths.get("/baeldung/authors/articles");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);
 
    assertEquals("authors\\articles", p1_rel_p2.toString());
    assertEquals("..\\..", p2_rel_p1.toString());
}

10. Сравнение путей

Класс Path имеет интуитивно понятную реализацию метода equals, который позволяет нам сравнивать два пути на предмет равенства:

@Test
public void givenTwoPaths_whenTestsEquality_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");
    Path p2 = Paths.get("/baeldung/articles");
    Path p3 = Paths.get("/baeldung/authors");

    assertTrue(p1.equals(p2));
    assertFalse(p1.equals(p3));
}

Вы также можете проверить, начинается ли путь с заданной строки:

@Test
public void givenPath_whenInspectsStart_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");
 
    assertTrue(p1.startsWith("/baeldung"));
}

Или заканчивается какой-либо другой строкой:

@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
    Path p1 = Paths.get("/baeldung/articles");
  
    assertTrue(p1.endsWith("articles"));
}

11. Заключение

В этой статье мы показали Операции с путями в новом API файловой системы (NIO2), который был представлен как часть Java 7 и видел большинство из них в действии.

Примеры кода, использованные в этой статье, можно найти в проекте статьи на Github.