File
. You can read about his work here . But in Java 7, the creators of the language decided to change how they work with files and directories. This was due to the fact that the class File
had a number of disadvantages. For example, it didn't have a method copy()
that would allow you to copy a file from one location to another (a seemingly clearly needed feature). In addition, the class File
had quite a lot of methods that returned boolean
-values. If an error occurs, such a method returns false rather than throwing an exception, which makes diagnosing errors and determining their causes very difficult. Instead of a single class, File
as many as 3 classes appeared: Paths
, Path
and Files
. Well, to be precise, Path
this is an interface, not a class. Let's figure out how they differ from each other and why each of them is needed. Let's start with the easiest thing - Paths
.
Paths
Paths
is a very simple class with a single static method get()
. It was created solely to obtain an object of type from the passed string or URI Path
. It has no other functionality. Here is an example of his work:
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
}
}
Not the most difficult class, right? :) Well, since we got an object of type Path
, let's figure out what it is Path
and why it is needed :)
Path
Path
, by and large, is a redesigned analogue of the File
. It is much easier to work with than with File
. Firstly , many utility (static) methods were removed from it and moved to the class Files
. Secondly , Path
the return values of the methods were ordered. In the class, File
the methods returned this String
, that boolean
, that File
- it was not easy to figure out. For example, there was a method getParent()
that returned the parent path for the current file as a string. But at the same time there was a method getParentFile()
that returned the same thing, but in the form of an object File
! This is clearly redundant. Therefore, in the interface, Path
the method getParent()
and other methods for working with files simply return an object Path
. No heaps of options - everything is easy and simple. What useful methods does it have Path
? Here are some of them and examples of their work:
-
getFileName()
— returns the file name from the path; -
getParent()
— returns the “parent” directory in relation to the current path (that is, the directory that is higher in the directory tree); -
getRoot()
— returns the “root” directory; that is, the one that is at the top of the directory tree; -
startsWith()
,endsWith()
— check whether the path begins/ends with the passed path:import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); Path fileName = testFilePath.getFileName(); System.out.println(fileName); Path parent = testFilePath.getParent(); System.out.println(parent); Path root = testFilePath.getRoot(); System.out.println(root); boolean endWithTxt = testFilePath.endsWith("Desktop\\testFile.txt"); System.out.println(endWithTxt); boolean startsWithLalala = testFilePath.startsWith("lalalala"); System.out.println(startsWithLalala); } }
Console output:
testFile.txt
C:\Users\Username\Desktop
C:\
true
falsePay attention to how the method works
endsWith()
. It checks if the current path ends with the passed path . It is on the path , and not on the set of characters .Compare the results of these two calls:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath.endsWith("estFile.txt")); System.out.println(testFilePath.endsWith("Desktop\\testFile.txt")); } }
Console output:
false
trueYou need to pass the full path to the method
endsWith()
, and not just a set of characters: otherwise the result will always be false , even if the current path actually ends with such a sequence of characters (as in the case of “estFile.txt” in the example above).In addition, there
Path
is a group of methods that simplifies working with absolute (full) and relative paths .
-
boolean isAbsolute()
— returns true if the current path is absolute:import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath.isAbsolute()); } }
Console output:
true
-
Path normalize()
— “normalizes” the current path, removing unnecessary elements from it. You may know that popular operating systems often use “.” characters when denoting paths. (“current directory”) and “..” (parent directory). For example: “ ./Pictures/dog.jpg ” means that in the directory we are in now, there is a Pictures folder, and in it there is a file “dog.jpg”So here it is. If your program has a path that uses “.” or “..”, the method
normalize()
will remove them and get a path that will not contain them:import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path path5 = Paths.get("C:\\Users\\Java\\.\\examples"); System.out.println(path5.normalize()); Path path6 = Paths.get("C:\\Users\\Java\\..\\examples"); System.out.println(path6.normalize()); } }
Console output:
C:\Users\Java\examples
C:\Users\examples -
Path relativize()
— calculates the relative path between the current and passed path.For example:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath1 = Paths.get("C:\\Users\\Users\\Users\\Users"); Path testFilePath2 = Paths.get("C:\\Users\\Users\\Users\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath1.relativize(testFilePath2)); } }
Console output:
Username\Desktop\testFile.txt
Path
is quite large. You can find them all in the Oracle documentation . We'll move on to review Files
.
Files
Files
- this is a utility class where static methods from the class were moved File
. Files
- this is approximately the same as Arrays
or Collections
, only it works with files, and not with arrays and collections :) It is focused on managing files and directories. Using static methods Files
, we can create, delete and move files and directories. For these operations the methods are used createFile()
(for directories - createDirectory()
), move()
and delete()
. Here's how to use them:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Main {
public static void main(String[] args) throws IOException {
//file creation
Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
System.out.println("Was the file created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
// create directory
Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
System.out.println("Was the directory successfully created?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));
//move file from desktop to testDirectory. You need to move with the name of the file in the folder!
testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Is our file left on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Has our file been moved to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
//remove file
Files.delete(testFile1);
System.out.println("Does the file still exist?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
}
}
Here we first create a file (method Files.createFile()
) on the desktop, then create a folder there (method Files.createDirectory()
). After that, we move the file (method Files.move()
) from the desktop to this new folder, and at the end we delete the file (method Files.delete()
). Console output: Was the file created successfully? true Was the directory created successfully? true Is our file still on the desktop? false Has our file been moved to testDirectory? true Does the file still exist? false Pay attention:Just like interface methods Path
, many methods Files
return an objectPath
. Most class methods Files
also accept Path
. Here a method will become your faithful assistant Paths.get()
- use it actively. What else is interesting in Files
? What the old class really lacked was the ! File
method. copy()
We talked about him at the beginning of the lecture, now is the time to meet him!
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Main {
public static void main(String[] args) throws IOException {
//file creation
Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
System.out.println("Was the file created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
// create directory
Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
System.out.println("Was the directory successfully created?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));
//copy the file from the desktop to the directory testDirectory2.
testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Is our file left on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Has our file been copied to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
}
}
Console output: Was the file created successfully? true Was the directory created successfully? true Is our file still on the desktop? true Was our file copied to testDirectory? true Now you can copy files programmatically! :) But the class Files
allows you not only to manage the files themselves, but also to work with its contents. To write data to a file, it has a method write()
, and for reading - as many as 3:, read()
and readAllBytes()
We readAllLines()
will dwell on the latter in detail. Why on it? Because it has a very interesting return type - List<String>
! That is, it returns us a list of lines in the file. Of course, this makes working with the content very convenient, because the entire file, line by line, can, for example, be output to the console in a regular loop for
:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Main {
public static void main(String[] args) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);
for (String s: lines) {
System.out.println(s);
}
}
}
Console output: I remember a wonderful moment: You appeared before me, Like a fleeting vision, Like a genius of pure beauty. Very comfortably! :) This feature appeared in Java 7. In Java 8, Stream API appeared , which added some elements of functional programming to Java. Including richer capabilities for working with files. Imagine that we have a task: find all lines in a file that begin with the word “How”, convert them to UPPER CASE and output them to the console. What would a solution using a class Files
in Java 7 look like? Something like this:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Main {
public static void main(String[] args) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);
List<String> result = new ArrayList<>();
for (String s: lines) {
if (s.startsWith("How")) {
String upper = s.toUpperCase();
result.add(upper);
}
}
for (String s: result) {
System.out.println(s);
}
}
}
Console output: LIKE A FASTING VISION, LIKE A GENIUS OF PURE BEAUTY. We seem to have done it, but don't you think that for such a simple task our code turned out to be a little... verbose? Using the Java 8 Stream API the solution looks much more elegant:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) throws IOException {
Stream<String> stream = Files.lines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"));
List<String> result = stream
.filter(line -> line.startsWith("How"))
.map(String::toUpperCase)
.collect(Collectors.toList());
result.forEach(System.out::println);
}
}
We achieved the same result, but with much less code! Moreover, it cannot be said that we have lost in “readability”. I think you can easily comment on what this code does, even if you are not familiar with the Stream API. But in short, a Stream is a sequence of elements on which you can perform different functions. We get the Stream object from the method Files.lines()
and then apply 3 functions to it:
-
Using the method,
filter()
we select only those lines from the file that begin with “How”. -
We go through all the selected rows using the method
map()
and reduce each of them to UPPER CASE. -
We combine all the resulting lines into
List
using thecollect()
.
Files.walkFileTree()
. Here's what we need to do. First, we need FileVisitor
. FileVisitor
is a special interface that describes all the methods for traversing the file tree. Specifically, we'll put logic there to read the contents of the file and check whether it contains the text we need. This is what ours will look like FileVisitor
:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
public class MyFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
List<String> lines = Files.readAllLines(file);
for (String s: lines) {
if (s.contains("This is the file we need")) {
System.out.println("Required file found!");
System.out.println(file.toAbsolutePath());
break;
}
}
return FileVisitResult.CONTINUE;
}
}
In this case, our class inherits from SimpleFileVisitor
. This is a class that implements FileVisitor
, in which you need to override only one method: visitFile()
. Here we describe what needs to be done with each file in each directory. If you need more complex traversal logic, you should write your own implementation FileVisitor
. There you will need to implement 3 more methods:
-
preVisitDirectory()
— logic that must be executed before entering the folder; -
visitFileFailed()
— what to do if entering the file is impossible (no access, or other reasons); -
postVisitDirectory()
— the logic that must be executed after entering the folder.
SimpleFileVisitor
. The logic inside the method visitFile()
is quite simple: read all lines from the file, check if they contain the content we need, and if so, print the absolute path to the console. The only line that might give you trouble is this one:
return FileVisitResult.CONTINUE;
In fact, everything is simple. Here we simply describe what the program should do after the file is entered and all the necessary operations are completed. In our case, we need to continue traversing the tree, so we choose the option CONTINUE
. But we, for example, could have another task: to find not all files that contain “This is the file we need”, but only one such file . After this, the program must be terminated. In this case, our code would look exactly the same, but instead of break; would:
return FileVisitResult.TERMINATE;
Well, let's run our code and see if it works.
import java.io.IOException;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Files.walkFileTree(Paths.get("C:\\Users\\Username\\Desktop\\testFolder"), new MyFileVisitor());
}
}
Console output: The required file has been found! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt The required file was found! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt The required file was found! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt Great, we did it! :) If you want to know more about walkFileTree()
, I recommend you this article . You can also complete a small task - replace it SimpleFileVisitor
with a regular one FileVisitor
, implement all 4 methods and come up with a purpose for this program. For example, you can write a program that will log all your actions: display the name of a file or folder in the console before/after entering them. That's all - see you later! :)
GO TO FULL VERSION