Використання інтерфейсу Path
Java 7 представляє нову абстракцію для шляху, а саме інтерфейс Path. Він використовується в нових функціях і API, по всьому NIO.2. Об'єкт шляху містить імена каталогів та файлів, які складають повний шлях до файлу / каталогу, представленого об'єктом Path; Path містить методи для вилучення елементів шляху, маніпуляцій з ними і їх додавання.
Нижче наведено приклад коду, для виконання на Unux-системі, але користувачі Windows можуть розкоментувати один рядок і закоментувати інший, для виконання прикладу на своїх машинах (див. Коментарі в коді). Існування відповідних файлів і катологів (test і testfile.txt) в файлової системі не обов'язково.
У цьому прикладі створюється об'єкт Path і витягується основна інформація, пов'язана з ним:
packagetest;importjava.nio.file.*;public classTest1 {public static voidmain(String[] args) {// Створення об'єкта Path через виклик статичного методу get() класу PathsPath testFilePath = Paths.get("/home/heorhi/testfile.txt");//Приклад рядка створення об'єкта Path шляху для запуску в Windows //Path testFilePath = Paths.get("D:\\test\\testfile.txt"); //Виведення інформації про файлSystem.out.println("Printing file information: ");System.out.println("\t file name: "+ testFilePath.getFileName());System.out.println("\t root of the path: "+ testFilePath.getRoot());System.out.println("\t parent of the target: "+ testFilePath.getParent());//Вивід елементів шляхуSystem.out.println("Printing elements of the path: ");for(Path element : testFilePath) {System.out.println("\t path element: "+ element);} } }
Вивід такий:
Printing file information:
file name: testfile.txt
root of the path: /
parent of the target: /home/heorhi
Printing elements of the path:
path element: home
path element: heorhi
path element: testfile.txt
Пояснення:- Спочатку створюється об'єкт класу Path, з використанням методу get () класу. Даний метод приймає рядок, що містить шлях.
- Зверніть увагу на використання символу '\' в Paths.get ( "D: \\ test \\ testfile.txt") для Windows-систем. Без нього '\ t' буде інтерпретуватися як символ табуляції, що призведе до java.nio.file.InvalidPathException при запуску програми, тому що символи табуляції не можуть міститися в шляхах.
- Потім відбувається вилучення імені файлу з використанням методу getFilename () об'єкта Path
- Далі використовується метод getRoot () для отримання кореневого елемента об'єкта Path і метод getParent () для отримання батьківської директорії цільового файлу.
- Наприкінці даного прикладу відбувається обхід елементів шляху за допомогою циклу foreach. Як альтернативу можна використовувати звичайний цикл і методи getNameCount () (для отримання числа елементів в дорозі) і getName (index) (для отримання елемента за індексом).
package test;
import java.io.IOException;
import java.nio.file.*;
class Test2 {
public static void main(String[] args) throws IOException {
Path testFilePath = Paths.get("./Test");
//Приклад шляху в Windows
//Path testFilePath = Paths.get(".\\Test");
System.out.println("The file name is: " + testFilePath.getFileName());
System.out.println("It's URI is: " + testFilePath.toUri());
System.out.println("It's absolute path is: "
+ testFilePath.toAbsolutePath());
System.out.println("It's normalized path is: "
+ testFilePath.normalize());
//// Отримання іншого об'єкта рядка за нормалізованим відносним шляхом
Path testPathNormalized = Paths
.get(testFilePath.normalize().toString());
System.out.println("It's normalized absolute path is: "
+ testPathNormalized.toAbsolutePath());
System.out.println("It's normalized real path is: "
+ testFilePath.toRealPath(LinkOption.NOFOLLOW_LINKS));
}
}
Пояснення до коду:- Метод toUri () повертає URI (шлях який може бути відкритий з браузера).
- Метод toAbsolutePath () повертає абсолютний шлях від даного відносного шляху. У разі, якщо був введений абсолютний шлях, метод поверне його ж.
- Метод normalize () виконує нормалізацію шляху, іншими словами видаляє непотрібні символи (такі як "." І "..") з об'єкта Path.
- Метод toRealPath () повертає абсолютний шлях від отриманого шляху (як toAbsolutePath ()) і нормалізує його (як normalize ()). Крім того, якщо всі параметри обрані правильно, то він може навіть працювати з символьними посиланнями. Однак, для цього методу необхідно, щоб кінцевий файл / каталог існував в файлової системі (це не є обов'язковою умовою для інших методів Path).
Приклад виведення при виконанні даного коду (файл Test не повинен існувати в файлової системі для аналогічного висновку):
The file name is: Test
It's URI is: file:///home/heorhi/workspace/OCPJP/./Test
It's absolute path is: /home/heorhi/workspace/OCPJP/./Test
It's normalized path is: Test
It's normalized absolute path is: /home/heorhi/workspace/OCPJP/Test
Exception in thread "main" java.nio.file.NoSuchFileException: /home/heorhi/workspace/OCPJP/Test
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixPath.toRealPath(UnixPath.java:876)
at test.Test2.main(Test2.java:23)
(Користувачі Windows-систем отримають sun.nio.fs.WindowsException.translateToIOException замість sun.nio.fs.UnixException.translateToIOException і т. Д. І т.п.)
Приклад виведення при виконанні даного коду (файл Test повинен існувати в файловій системі для аналогічного висновку):
The file name is: Test
It's URI is: file:///home/heorhi/workspace/OCPJP/./Test/
It's absolute path is: /home/heorhi/workspace/OCPJP/./Test
It's normalized path is: Test
It's normalized absolute path is: /home/heorhi/workspace/OCPJP/Test
It's normalized real path is: /home/heorhi/workspace/OCPJP/Test
Інтерфейс Path містить два методи для порівняння об'єктів Path: equals () and compareTo (). Метод equals () порівнює шляхи і повертає Boolean. Метод compareTo () порівнює шляхи посимвольний і повертає: 0, якщо шляху рівні; негативне ціле значення, якщо шлях в об'єкті викликає метод лексикографічно менше шляху в об'єкті, переданому в якості параметра; натуральне значення в протилежному випадку.
Приклад коду:package test;
import java.nio.file.*;
class Test3 {
public static void main(String[] args) {
Path path1 = Paths.get("Test");
Path path2 = Paths.get("/home/heorhi/workspace/OCPJP/Test");
System.out.println("(path1.compareTo(path2) == 0) is: "
+ (path1.compareTo(path2) == 0));
System.out.println("path1.equals(path2) is: " + path1.equals(path2));
System.out.println("path2.equals(path1.toAbsolutePath()) is "
+ path2.equals(path1.toAbsolutePath()));
System.out.println(path1.toAbsolutePath());
}
}
Останній
System.out.println є підказкою — він виводить шлях,який повинен бути переданий вpath2 для аналогічного виведення. Виведення таке:(path1.compareTo(path2) == 0) is: false
path1.equals(path2) is: false
path2.equals(path1.toAbsolutePath()) is true
/home/heorhi/workspace/OCPJP/Test
Використання класу Files
Розглянемо клас Files (введений в Java 7, знаходиться в пакеті java.nio.file), який можна використовувати для виконання різних операцій з файлами та каталогами. Files є службовим класом, це означає, що це final-клас з private-конструктором і містить тільки статичні методи. В цьому класі знаходиться безліч методів для виконання різних дій. Розглянемо деякі з них.
Вище був показаний приклад коду, в якому з'ясовувалося, чи вказують два шляхи на один файл. Існує спосіб перевірити це за допомогою методу isSameFile () з класу Files:
package test;import java.io.IOException; import java.nio.file.*; class Test4 { public static void main(String[] args) throws IOException { Path path1 = Paths.get("Test"); Path path2 = Paths.get("/home/heorhi/workspace/OCPJP/Test"); System.out.println("Files.isSameFile(path1, path2) is: " + Files.isSameFile(path1, path2)); } }
Оскільки тут порівнюються файли, а не шляхи, то існування відповідного файлу обов'язково (див. підказку для шляху в попередньому прикладі) інакше буде отримана помилка
java.nio.file.NoSuchFileException.
У разі, якщо файл за вказаною адресою існує, отримаємо такий висновок:
Files.isSameFile(path1, path2) is: true
Можна визначити, маємо ми справу з файлом або Директорією (папкою) за допомогою методу isDirectory () класу Files і перевірити їх існування за допомогою методу exists ():
package test;
import java.nio.file.*;
class Test5 {
public static void main(String[] args) {
//Проверка для файла
Path path = Paths.get("/home/heorhi/workspace/OCPJP/src/test/Test5.java");
//Перевірка для директорії
//Path path = Paths.get("/home/heorhi/workspace/OCPJP/src/test");
if (Files.exists(path, LinkOption.NOFOLLOW_LINKS)) {
System.out.println("The file/directory " + path.getFileName()
+ " exists");
if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
System.out.println(path.getFileName() + " is a directory");
} else {
System.out.println(path.getFileName() + " is a file");
}
} else {
System.out.println("The file/directory " + path.getFileName() + " does not exist");
}
}
}
Висновок повинен вийде приблизно такий (вибирайте шляху відповідно до вашої ОС і розташуванням файлів):
The file/directory Test5.java exists
Test5.java is a file
Цікавий висновок можна отримати, якщо написати
Path path = Paths.get("/"); для Unix-систем абоPath path = Paths.get("С:\\"); для Windows-систем, тобто якщо передати в якості параметра ім'я кореневого каталогу:The file/directory null exists
null is a directory
Для корневого каталогу
Клас Files містить методи isReadable (), isWriteable () і isExecutable () для перевірки можливості читання, записи і виконання файлів:path.getFileName() повертається null.package test;
import java.nio.file.*;
class Test6 {
public static void main(String[] args) {
Path path = Paths.get("Вставьте сюда путь к какому-либо файлу");
System.out.printf("Readable: %b, Writable: %b, Executable: %b ",
Files.isReadable(path), Files.isWritable(path),
Files.isExecutable(path));
}
}
Метод getAttribute () дозволяє отримати властивості (атрибути) файлу. Метод приймає змінне число параметрів: перший - об'єкт Path; другий - ім'я атрибута; далі від нуля до декількох значень LinkOption (це enum):
package test;
import java.io.IOException;
import java.nio.file.*;
class Test7 {
public static void main(String[] args) {
Path path = Paths.get("Вставьте сюда путь к какому-либо файлу");
try {
Object object = Files.getAttribute(path, "creationTime");
System.out.println("Creation time: " + object);
//Здесь указан третий параметр
object = Files.getAttribute(path, "lastModifiedTime",
LinkOption.NOFOLLOW_LINKS);
System.out.println("Last modified time: " + object);
object = Files.getAttribute(path, "size");
System.out.println("Size: " + object);
object = Files.getAttribute(path, "isDirectory");
System.out.println("isDirectory: " + object);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Копіювання файлів
Тепер розглянемо копіювання файлу / ДИРЕКТОРІЇ. Для цього використовуємо метод Files.copy (). Сигнатура даного методу:
Path copy(Path source, Path target, CopyOption. . . options)
Перший параметр - шлях до вихідного файлу, другий - шлях до того файлу, що буде створено в результаті копіювання (включаючи ім'я нового файлу), далі можна задати параметри копіювання, а можна і не поставити, як в прикладі нижче:
package test;
import java.io.IOException;
import java.nio.file.*;
public class Test8 {
public static void main(String[] args) {
Path pathSource = Paths.get("Вставте сюди шлях до файлу / директорії для копіювання");
Path pathDestination = Paths.get("Вставте сюди шлях для нового файлу / директорії");
try {
Files.copy(pathSource, pathDestination);
System.out.println("Source file copied successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Після першого запуску все повинно відпрацювати коректно для коректних шляхів. Але якщо даний код без змін скомпілювати і виконати повторно, то буде отримано виключення
java.nio.file.FileAlreadyExistsException. Воно пов'язане з тим, що цільовий файл вже існує. Для уникнення таких проблем можна вказати, щоб в разі його існування його перезаписувати. Для цього треба трохи змінити один рядок коду:Files.copy(pathSource, pathDestination, StandardCopyOption.REPLACE_EXISTING);
Слід зазначити, що при копіюванні директор не будуть копіюватися містяться в ній файли і директорії. Це виглядає безглуздо - нижче буде показано, як це виправити.
Переміщення файлу
Метод для переміщення файлу дуже схожий на метод для копіювання:Path move(Path source, Path target, CopyOption. . . options)
Значення переданих параметрів збігаються за змістом. Приклад коду відрізняється від попереднього мінімально:
package test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class Test9 {
public static void main(String[] args) {
Path pathSource = Paths.get("Вставте сюди шлях до файлу / директорії, який треба перемістити");
Path pathDestination = Paths.get("Вставте сюди шлях для нового місця розташування файлу / директорії");
try {
Files.move(pathSource, pathDestination, StandardCopyOption.REPLACE_EXISTING);
System.out.println("Source file copied successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Якщо при копіюванні директорії містяться в ній файли і директорії не копіювались, то при її переміщенні, в разі відсутності помилок, переміщається і весь вміст.
Видалення файлу
Подивимося на приклад коду:package test;
import java.io.IOException;
import java.nio.file.*;
public class Test10 {
public static void main(String[] args) {
Path pathSource = Paths.get("Вставте сюди шлях до файлу / директорії для видалення");
try {
Files.delete(pathSource);
System.out.println("File deleted successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Кілька моментів, які необхідно пам'ятати щодо методу Files.delete ():
- У разі видалення каталогу, необхідно, щоб він був порожнім, інакше буде отримано виключення
(java.nio.file.DirectoryNotEmptyException) - Для даного методу необхідно, щоб файл існував, інакше буде отримано виключення
java.nio.file.NoSuchFileException). - Якщо необхідно ігнорувати дані випадки, то краще підійде метод deleteIfExists (), який видаляє файл в разі його існування і не кидає такий виняток при його відсутності.
Якщо передати в даний метод символьне посилання, то буде вилучено посилання, а не цільовий файл.
Обхід дерева файлів
При роботі з файлової системою може виникнути необхідність обходу дерева файлів, наприклад при пошуку файлу або копіюванні каталогу з усім його вмістом. Клас Files містить два методи, що дозволяють обходити дерево файлів. Їх сигнатури наведені нижче:
Path walkFileTree(Path start, FileVisitor<!--? super Path--> visitor)
Path walkFileTree(Path start, Set<filevisitoption> options, int maxDepth, FileVisitor<!--? super Path--> visitor)
Обидва методи приймають шлях, з якого почнеться обхід дерева і екземпляр типу FileVisitor, який буде визначати поведінку при обході дерева. Другий метод має два додаткові параметри: Set, що містить опції обходу, і максимальну глибину. Максимальна глибина визначає, наскільки рівнів каталогів відбуватиметься обхід. Якщо в її якості вказати 0, то буде розглядатися тільки вказаний файл, а якщо вказати MAX_VALUE, то будуть пройдені всі підкаталоги.
FileVisitor — це інтерфейс, який містить наступні методи:
FileVisitResult preVisitDirectory(T dir, BasicFileAttributesattrs)— виконується перед доступом до елементів каталогу.FileVisitResult visitFile(T file, BasicFileAttributes attrs)— виконується при доступі до файлу.FileVisitResult postVisitDirectory(T dir, IOException exc)— виконується, коли всі елементи директорії пройдені.FileVisitResult visitFileFailed(T file, IOException exc)— виконується, якщо до файлу немає доступу.
Вам необхідно реалізувати інтерфейс FileVisitor, щоб передати відповідний об'єкт в метод walkFileTree (). Але якщо необхідності реалізовувати всі чотири методи цього інтерфейсу немає, то можна просто розширити реалізацію класу SimpleFileVisitor, перевизначивши лише необхідні методи.
Приклад:package test;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
class MyFileVisitor extends SimpleFileVisitor {
public FileVisitResult visitFile(Path path,
BasicFileAttributes fileAttributes) {
System.out.println("file name:" + path.getFileName());
return FileVisitResult.CONTINUE;
}
public FileVisitResult preVisitDirectory(Path path,
BasicFileAttributes fileAttributes) {
System.out.println("Directory name:" + path);
return FileVisitResult.CONTINUE;
}
}
public class Test11 {
public static void main(String[] args) {
Path pathSource = Paths.get("Введіть сюди шлях до будь-якого каталогу, який містить інші каталоги та файли");
try {
Files.walkFileTree(pathSource, new MyFileVisitor());
} catch (IOException e) {
e.printStackTrace();
}
}
}
При виконанні даного коду будуть виведені всі вкладені каталоги і файли за вказаним шляхом. Ось що слід зрозуміти:- Оголошується клас MyFileVisitor, успадкований від SimpleFileVisitor, в якому перевизначені два методи: visitFile () (для виведення імені файлу) і preVisitDirectory () (для виведення імені директорії).
- Викликається walkFileTree () в який передається об'єкт MyFileVisitor.
- Метод walkFileTree () починає виконання з переданого в нього каталогу. При цьому викликається метод visitFile () при кожному проході файлу, preVisitDirectory () - перед переглядом елементів директорії, postVisitDirectory () - після перегляду елементів директорії, visitFileFailed () - в разі відсутності доступу до файлу / директорії.
- З цих чотирьох методів були перевизначені тільки два для виведення імен каталогів і файлів.
- Можна контролювати потік обходу за допомогою повертаються цими методами значень (enum FileVisitResult). Їх чотири:
- CONTINUE: вказує на те, що обхід дерева слід продовжити.
- TERMINATE: вказує, що обхід потрібно негайно припинити.
- SKIP_SUBTREE: вказує, що підкаталоги повинні бути пропущені для обходу.
- SKIP_SIBLINGS: вказує на те, що обхід повинен бути зупинений в поточному каталозі і каталогах одного рівня з ним. Якщо це значення повертається з preVisitDirectory (), то вкладені файли / каталоги не обходяться і postVisitDirectory () не спрацьовує. Якщо це значення повертається з visitFile (), то інші файли каталогу не обходяться. Якщо він повертається з postVisitDirectory (), то інші каталоги того ж рівня не будуть обходитися.
package test;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.*;
class MyFileCopyVisitor extends SimpleFileVisitor {
private Path source, destination;
public MyFileCopyVisitor(Path s, Path d) {
source = s;
destination = d;
}
public FileVisitResult visitFile(Path path,
BasicFileAttributes fileAttributes) {
Path newd = destination.resolve(source.relativize(path));
try {
Files.copy(path, newd, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
public FileVisitResult preVisitDirectory(Path path,
BasicFileAttributes fileAttributes) {
Path newd = destination.resolve(source.relativize(path));
try {
Files.copy(path, newd, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
}
public class Test12 {
public static void main(String[] args) {
Path pathSource = Paths.get("Вставте сюди шлях до бажано не порожній директорії для копіювання");
Path pathDestination = Paths.get("Вставте сюди шлях до нового положення директорії");
try {
Files.walkFileTree(pathSource, new MyFileCopyVisitor(pathSource, pathDestination));
System.out.println("Files copied successfully!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
У методі preVisitDirectory () відбувається копіювання відвідуваного каталогу і аналогічно копіюється файл в метод visitFile (). Щоб отримати новий шлях призначення, використовується метод relativize () з класу Path.
P.S. Не плутайте File з Files, Path з Paths, і FileSystem c FileSystems. File старий клас (Java 4), а Files був введений в Java 7. Інтерфейс Path є шлях до файлу / каталогу і визначає корисний список методів, а клас Paths є службовим класом, який надає два методи для отримання об'єкта типу Path. Клас FileSystems надає список фабричних методів для отримання класу FileSystem, в той час як FileSystem надає набір методів, для отримання інформації про файлову систему.