Рубрики
Без рубрики

Java NIO2 Patch API

Краткое и практическое руководство по API Path в Java

Автор оригинала: baeldung.

1. Обзор

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

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

2. Настройка

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

import java.nio.file.*;

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

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

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

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

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

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

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

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

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

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 может принимать переменный параметр аргументов частей строки пути (в данном случае articles и baeldung ) в дополнение к первой части (в данном случае articles ).

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

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

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

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

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

@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());
}

Каждый путь связан с родительским путем или null , если у пути нет родителя. Родительский объект path состоит из корневого компонента пути, если таковой имеется, и каждого элемента в пути, за исключением имени файла. Например, родительский путь /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/статьи . Первые два имеют избыточность, а последний-нет.

Нормализация пути включает в себя устранение избыточности в нем. Для этой цели предусмотрена операция 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 . Этот метод пытается разрешить путь путем сопоставления его элементов с фактическими каталогами и файлами в файловой системе.

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

@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. Пути соединения

Объединение любых двух путей может быть достигнуто с помощью метода resolve .

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

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

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

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

@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 являются допустимыми путями.

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

@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());
}

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

@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 .