Використання інтерфейсу Path
Java 7 представляє нову абстракцію для шляху, а саме інтерфейс Path. Він використовується в нових функціях і API, по всьому NIO.2. Об'єкт шляху містить імена каталогів та файлів, які складають повний шлях до файлу / каталогу, представленого об'єктом Path; Path містить методи для вилучення елементів шляху, маніпуляцій з ними і їх додавання.
Нижче наведено приклад коду, для виконання на Unux-системі, але користувачі Windows можуть розкоментувати один рядок і закоментувати інший, для виконання прикладу на своїх машинах (див. Коментарі в коді). Існування відповідних файлів і катологів (test і testfile.txt) в файлової системі не обов'язково.
У цьому прикладі створюється об'єкт Path і витягується основна інформація, пов'язана з ним:
package
test;import
java.nio.file.*;public class
Test1 {public static void
main(String[] args) {// Створення об'єкта Path через виклик статичного методу get() класу Paths
Path 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 надає набір методів, для отримання інформації про файлову систему.