1. Обзор
В этой статье мы узнаем, как использовать новый API пути ввода-вывода (NIO2) в
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. Создание пути
Чтобы создать объект Path
из строки пути:
@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
Path p = Paths.get("/articles/foreach");
assertEquals("\\articles\\foreach", p.toString());
}
Get
API может принимать переменные аргументы частей строки пути (в данном случае статьи
и foreach
) в дополнение к первой части (в данном случае статьи
).
Если мы предоставим эти части вместо полной строки пути, они будут использоваться для построения объекта Path, нам не нужно включать разделители имен (косые черты) в часть переменных аргументов:
@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
Path p = Paths.get("/articles", "foreach");
assertEquals("\\articles\\foreach", p.toString());
}
5. Получение информации о пути
Вы можете думать об объекте Path как об элементах имени как о последовательности. Строка
пути , такая как E:\foreach\articles\java
,
состоит из трех элементов имени, т . е . foreach
, article и java
. Самый высокий элемент в структуре каталогов будет располагаться по индексу 0, в данном случае это foreach
.
``
Самый нижний элемент в структуре каталогов будет располагаться по индексу [n-1]
, где n
— количество элементов имени в пути. Этот самый нижний элемент называется именем файла независимо от того, является ли он фактическим файлом или нет:
@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
Path p = Paths.get("/articles/foreach/logs");
Path fileName = p.getFileName();
assertEquals("logs", fileName.toString());
}
Доступны методы для получения отдельных элементов по индексу:
@Test
public void givenPath_whenRetrievesNameByIndex_thenCorrect() {
Path p = Paths.get("/articles/foreach/logs");
Path name0 = getName(0);
Path name1 = getName(1);
Path name2 = getName(2);
assertEquals("articles", name0.toString());
assertEquals("foreach", name1.toString());
assertEquals("logs", name2.toString());
}
или подпоследовательность пути с использованием этих диапазонов индексов:
@Test
public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() {
Path p = Paths.get("/articles/foreach/logs");
Path subPath1 = p.subpath(0,1);
Path subPath2 = p.subpath(0,2);
assertEquals("articles", subPath1.toString());
assertEquals("articles\\foreach", subPath2.toString());
assertEquals("articles\\foreach\\logs", p.subpath(0, 3).toString());
assertEquals("foreach", p.subpath(1, 2).toString());
assertEquals("foreach\\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/foreach/logs");
Path p2 = Paths.get("/articles/foreach");
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\\foreach", parent1.toString());
assertEquals("\\articles", parent2.toString());
assertEquals("\\", parent3.toString());
assertEquals(null, parent4);
}
Мы также можем получить корневой элемент пути:
@Test
public void givenPath_whenRetrievesRoot_thenCorrect() {
Path p1 = Paths.get("/articles/foreach/logs");
Path p2 = Paths.get("c:/articles/foreach/logs");
Path root1 = p1.getRoot();
Path root2 = p2.getRoot();
assertEquals("\\", root1.toString());
assertEquals("c:\\", root2.toString());
}
6. Нормализация пути
Многие файловые системы используют «.»
для обозначения текущего каталога и «..»
для обозначения родительского каталога. Может возникнуть ситуация, когда путь содержит избыточную информацию о каталоге.
Например, рассмотрим следующие строки пути:
/foreach/./articles
/foreach/authors/../articles
/foreach/articles
Все они разрешаются в одно и то же место /foreach/articles
. Первые два имеют избыточность, а последний - нет.
Нормализация пути включает удаление в нем избыточности. Для этой цели предусмотрена операция Path.normalize() .
Этот пример теперь должен быть понятен сам по себе:
@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
Path p = Paths.get("/home/./foreach/articles");
Path cleanPath = p.normalize();
assertEquals("\\home\\foreach\\articles", cleanPath.toString());
}
Этот тоже:
@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
Path p = Paths.get("/home/foreach/../articles");
Path cleanPath = p.normalize();
assertEquals("\\home\\articles", cleanPath.toString());
}
7. Преобразование пути
Существуют операции преобразования пути в выбранный формат представления. Чтобы преобразовать любой путь в строку, которую можно открыть из браузера, мы используем метод toUri
:
@Test
public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
Path p = Paths.get("/home/foreach/articles.html");
URI uri = p.toUri();
assertEquals(
"file:///E:/home/foreach/articles.html",
uri.toString());
}
Мы также можем преобразовать путь в его абсолютное представление. Метод toAbsolutePath
разрешает путь относительно каталога файловой системы по умолчанию:
@Test
public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
Path p = Paths.get("/home/foreach/articles.html");
Path absPath = p.toAbsolutePath();
assertEquals(
"E:\\home\\foreach\\articles.html",
absPath.toString());
}
Однако, когда обнаруживается, что путь, который нужно разрешить, уже является абсолютным, метод возвращает его как есть:
@Test
public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
Path p = Paths.get("E:\\home\\foreach\\articles.html");
Path absPath = p.toAbsolutePath();
assertEquals(
"E:\\home\\foreach\\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\\foreach\\articles.html");
p.toRealPath();
}
Тест завершается успешно, когда мы перехватываем IOException
. Фактическим подклассом IOException
, который выдает эта операция, является NoSuchFileException
.
8. Объединение путей
Соединение любых двух путей может быть достигнуто с помощью метода разрешения
.
Проще говоря, мы можем вызвать метод разрешения
для любого пути
и передать частичный путь
в качестве аргумента. Этот частичный путь добавляется к исходному пути:
@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
Path p = Paths.get("/foreach/articles");
Path p2 = p.resolve("java");
assertEquals("\\foreach\\articles\\java", p2.toString());
}
Однако, когда строка пути, переданная методу разрешения
, не является частичным путем;
в первую очередь абсолютный путь, тогда возвращается переданный путь:
@Test
public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() {
Path p = Paths.get("/foreach/articles");
Path p2 = p.resolve("C:\\foreach\\articles\java");
assertEquals("C:\\foreach\\articles\\java", p2.toString());
}
То же самое происходит с любым путем, имеющим корневой элемент. Строка пути «java»
не имеет корневого элемента, а строка пути «/java»
имеет корневой элемент. Поэтому, когда вы передаете путь с корневым элементом, он возвращается как есть:
@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
Path p = Paths.get("/foreach/articles");
Path p2 = p.resolve("/java");
assertEquals("\\java", p2.toString());
}
9. Релятивизация
путей
Термин релятивизация
просто означает создание прямого пути между двумя известными путями. Например, если у нас есть каталог /foreach
и внутри него, у нас есть два других каталога, так что /foreach/authors
и /foreach/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());
}
Предположим, что мы перемещаем каталог статей в папку
авторов
, чтобы они больше не были братьями и сестрами. Следующие релятивизирующие операции включают создание пути между foreach
и статьями
и наоборот:
@Test
public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() {
Path p1 = Paths.get("/foreach");
Path p2 = Paths.get("/foreach/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("/foreach/articles");
Path p2 = Paths.get("/foreach/articles");
Path p3 = Paths.get("/foreach/authors");
assertTrue(p1.equals(p2));
assertFalse(p1.equals(p3));
}
Вы также можете проверить, начинается ли путь с заданной строки:
@Test
public void givenPath_whenInspectsStart_thenCorrect() {
Path p1 = Paths.get("/foreach/articles");
assertTrue(p1.startsWith("/foreach"));
}
Или заканчивается другой строкой:
@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
Path p1 = Paths.get("/foreach/articles");
assertTrue(p1.endsWith("articles"));
}
11. Заключение
В этой статье мы показали операции Path в новом API файловой системы (NIO2), который был поставлен как часть Java 7, и увидели большинство из них в действии.
Примеры кода, использованные в этой статье, можно найти в проекте статьи на Github .