Say goodbye to low-efficiency IO, and make up a wave of Java 7's NIO.2 features

In fact, I have written an article about the new features of Java 7 before. That article mainly introduced the automatic shutdown of Java 7 resources, the implementation principle of Switch String, exception catch try-catch, new binary writing methods, etc. The content can also be read the original article ( a wave of Java 7 syntax features ). In that article, the only update to IO operations in Java 7 was not introduced, and this part happens to be a very important part. What should be paid back is always to be paid back, so I will add it now.

After reading this article, you will know:

  1. Abstract file path operation mode, intuitive, convenient and less BUG.
  2. Efficient file operation mode, only one line is required for writing, reading, and copying files .
  3. Quickly obtain file attributes under different systems .
  4. There are many ways to traverse files and directories under the directory , and it is very efficient.
  5. Reactive event notification to monitor file changes.

In Java 7, file operations related functions have been enhanced, that is, the contents of the new java.nio.file package, which provides functions such as file path abstraction, file directory flow, directory tree, file attributes, and change monitoring services. Can greatly improve our operations on files.

NIO.2

file path

Before Java 7, the operations on file paths were all operations with strings . When using it, you need to throw a string directly in. It is inefficient to use string operations directly. For example, if you want to splice the parent path and the subdirectory, you only Can be used for string splicing. And the splicing itself loses its meaning as a file path. In addition, using strings for various path operations is likely to cause various problems due to spelling errors.

The advent of Java 7 makes all this different. It provides the Path interface to represent the abstraction of the path, and then provides a series of operation methods for the path, making it so simple.

In order to facilitate the creation of Path objects, the Paths tool class is provided. Let us see how to use it.

Everything from Path path = Paths.get("/Users/darcy/java/");the beginning to get a Path object.

Path path = Paths.get("/Users/darcy/java/");
System.out.println("完整路径:" + path.toString());

Path pathParent = path.getParent();
System.out.println("父级路径:" + pathParent.toString());

Path pathRoot = path.getRoot();
System.out.println("根目录:" + pathRoot.toString());

int pathNameCount = path.getNameCount();
System.out.println("目录深度:" + pathNameCount);

Path pathIndex3 = path.getName(2);
System.out.println("第三级目录:" + pathIndex3);

Path subPath = path.subpath(1, 3);
System.out.println("第1级目录到第三级目录(包左不包右):" + subPath.toString());

// resolveSibling 从当前目录父目录开始拼接目录
Path pathResolveSibling = path.resolveSibling("PathDemo.java");
System.out.println("父目录开始拼接参数:" + pathResolveSibling.toString());

// resolve 把当前路径当作父路径,参数作为子目录或者文件
Path pathResolve = Paths.get("/Users/darcy/java/").resolve("PathDem.java");
System.out.println("当前目录拼接后的目录:" + pathResolve.toString());

// 参数路径相对于主体路径的相对路径
Path path1 = Paths.get("/Users/darcy/");
Path path2 = Paths.get("/Users/darcy/java/PathDemo.java");
Path path3 = path1.relativize(path2);
System.out.println("相对路径:" + path3.toString());

/* 输出结果
完整路径:/Users/darcy/java
父级路径:/Users/darcy
根目录:/
目录深度:3
第三级目录:java
第1级目录到第三级目录(包左不包右):darcy/java
父目录开始拼接参数:/Users/darcy/PathDemo.java
当前目录拼接后的目录:/Users/darcy/java/PathDem.java
相对路径:java/PathDemo.java
*/

You can see that in the above code, except for entering a path when creating the Path object, the subsequent operations are all performed using the methods in Path. Before that, you may need to intercept and splice various strings, which is very cumbersome.

File operations

I still remember that when I first learned Java IO, there were many ways to write file copy, but no matter which one, it requires a lot of code to write, and you also need to consider the performance of copying. Not to mention reading files, defining various reading and receiving variables, and various verifications. Now it's different. Not only file operations are very convenient, but also common operations like file copying and reading can be done in one line .

Too simple to use, direct code.

// 如果文件不存在,则创建一个文件
Path path = Paths.get("test.txt");
Path pathBackup = Paths.get("test_bak.txt");
Path pathLink = Paths.get("test.txt.link");
Path pathDir = Paths.get("dir");

// 已存在则删除
Files.deleteIfExists(path);
Files.deleteIfExists(pathBackup);
Files.deleteIfExists(pathLink);
Files.deleteIfExists(pathDir);

// 创建文件写入内容
Path file = Files.createFile(path);
Files.write(path, "关注公众号:未读代码".getBytes());
Files.write(path, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
Files.write(path, "欢迎加我微信:wn8398".getBytes(), StandardOpenOption.APPEND);
System.out.println("创建文件:" + file.toString());

// 创建文件链接
pathLink = Files.createLink(pathLink, path);
System.out.println("创建文件:" + pathLink.toString());

// 创建目录
Path directory = Files.createDirectory(pathDir);
System.out.println("创建目录:" + directory.toString());

// 文件复制
Files.copy(path, pathBackup);
System.out.println("复制文件: " + path + " --> " + pathBackup);

// 读取文件
List<String> lines = Files.readAllLines(pathBackup);
for (String line : lines) {
    
    
    System.out.println("文件读取:" + line);
}

The above shows how to create, delete, write, copy, and read files of the Files class, all with only one line of code.

File attributes

Similar to path operations, Java 7 also provides the abstraction of file attributes, adding a series of file attribute operation tool classes. This code is in java.nio.file.attributethe bag. It abstracts an AttributeView as the parent interface of all attribute views, and then uses its subclass FileAttributeView to represent the file view, and its subclass FileOwnerAttributeView to represent the file owner's attribute view. The former attributes such as the creation time of the file, the modification time, whether it is a directory, etc., while the latter contains information about the file. In order to be compatible with different operating systems, Java 7 also provides different implementations, such as the DosFileAttributeView view, which is obviously prepared for the Windows operating system.

Java7 file attributes

It's too simple to use, direct code is provided.

Path path = Paths.get("/Users/darcy/git/jdk-feature/README.md");
BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes basicFileAttributes = fileAttributeView.readAttributes();
FileTime creationTime = basicFileAttributes.creationTime();
FileTime lastModifiedTime = basicFileAttributes.lastModifiedTime();
FileTime lastAccessTime = basicFileAttributes.lastAccessTime();
System.out.println("创建时间:" + creationTime);
System.out.println("上次修改时间:" + lastModifiedTime);
System.out.println("上次访问时间:" + lastAccessTime);

boolean directory = basicFileAttributes.isDirectory();
boolean regularFile = basicFileAttributes.isRegularFile();
boolean symbolicLink = basicFileAttributes.isSymbolicLink();
System.out.println("是否目录:" + directory);
System.out.println("是否普通文件:" + regularFile);
System.out.println("是否符号链接:" + symbolicLink);

long size = basicFileAttributes.size();
System.out.println("文件大小:" + size);

PosixFileAttributeView linuxFileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class);
UserPrincipal owner = linuxFileAttributeView.getOwner();
System.out.println("文件归属用户:" + owner.getName());

After running the sample code, you get the following output.

创建时间:2020-09-06T13:35:14Z
上次修改时间:2020-09-06T13:35:14.649261371Z
上次访问时间:2020-09-06T13:35:14.680968254Z
是否目录:false
是否普通文件:true
是否符号链接:false
文件大小:3636
文件归属用户:darcy

File list stream

To traverse file directories and files before Java 7, you should choose the listFiles method of the File class.

// 文件直接遍历,不会遍历子目录
String pathString = "/Users/darcy/project/mylab/src/main/java/com/wdbyte/java";
File file = new File(pathString);
File[] listFiles = file.listFiles();
for (File tempFile : listFiles) {
    
    
    System.out.println("file list: " + tempFile.getAbsolutePath());
}

This kind of traversal method also seems very elegant, but it will become very inefficient when facing a large number of files . So Java 7 has also improved this, introducing DirectoryStream file list stream. It can perform progressive file traversal, reading a certain amount at a time, reducing the performance overhead during traversal, but DirectoryStream only traverses its direct directories and files when traversing, and does not traverse subdirectories recursively. The following is its traversal writing.

String pathString = "/Users/darcy/project/mylab/src/main/java/com/wdbyte/java";
// Path 直接遍历方式,不会遍历子目录
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(pathString))) {
    
    
    for (Path pathTemp : directoryStream) {
    
    
        System.out.println("DirectoryStream: " + pathTemp);
    }
}

// Path 直接遍历方式 - 筛选 .class 文件
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(pathString), "*.java")) {
    
    
    for (Path pathTemp : directoryStream) {
    
    
        System.out.println("DirectoryStream file type is class : " + pathTemp);
    }
}

Here is an extension. The Files class has been enhanced in Java 8, the Lambda expression of Java 8 has been introduced, the walk method has been added, and traversing files has the same effect (Lambda expressions are used in the following example).

// 遍历所有目录和子目录
Stream<Path> pathStream = Files.walk(Paths.get("/Users/darcy/project/mylab/src/main/java/com/wdbyte"));
pathStream.forEach(pathTemp -> {
    
    
    System.out.println("Stream: " + pathTemp.toString());
});

// 遍历所有目录和子目录 - 筛选 java 文件
pathStream = Files.walk(Paths.get("/Users/darcy/project/mylab/src/main/java/com/wdbyte"));
pathStream
    .filter(pathTemp -> pathTemp.toString().endsWith(".java"))
    .forEach(pathTemp -> {
    
    
        System.out.println("Stream filter java: " + pathTemp.toString());
    });

File monitoring

File monitoring, that is, you can dynamically monitor the changes of files or content in a specified directory. There are many application scenarios, such as checking whether the class file is updated during hot deployment, or operating whenever a file comes in. Before this, you could only find the file changes by calling listFiles in a loop and comparing with the last call result, but now you can perform reactive logic processing through notifications, everything becomes simpler.

The monitored object must implement the Watchable interface, and then register to the implementation of the WatchService interface of the monitoring service through the register method, and specify the type of event to be monitored.

// 创建
StandardWatchEventKinds.ENTRY_CREATE,
// 删除
StandardWatchEventKinds.ENTRY_DELETE,
// 更新
StandardWatchEventKinds.ENTRY_MODIFY

How to use it? How to look through the following example code to achieve, the following code folder /Users/darcy/testmonitoring, registration of an event of interest is created, deleted or updated.

WatchService watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get("/Users/darcy/test");
path.register(watchService,
    StandardWatchEventKinds.ENTRY_CREATE,
    StandardWatchEventKinds.ENTRY_DELETE,
    StandardWatchEventKinds.ENTRY_MODIFY);

while (true) {
    
    
    WatchKey watchKey = watchService.take();
    // 获取事件类型
    for (WatchEvent<?> pollEvent : watchKey.pollEvents()) {
    
    
        // 具体的事件上下文信息
        Path tempPath = (Path)pollEvent.context();
        Kind<?> kind = pollEvent.kind();
        if (kind.name().equals(StandardWatchEventKinds.ENTRY_CREATE.name())) {
    
    
            System.out.println("创建了一个文件:" + tempPath.toString());
        }
        if (kind.name().equals(StandardWatchEventKinds.ENTRY_DELETE.name())) {
    
    
            System.out.println("删除了一个文件:" + tempPath.toString());
        }
        if (kind.name().equals(StandardWatchEventKinds.ENTRY_MODIFY.name())) {
    
    
            System.out.println("修改了一个文件:" + tempPath.toString());
        }
    }
    // 事件处理完毕后要进行 reset 才能继续监听事件
    watchKey.reset();
    // 取消监视
    // watchKey.cancel();
}

After registering the event listener, through a loop, call the take() method to get the event result, and then judge the event type for log output after getting the event. I did a simple test after booting, and the following is the log output.

# 下面是我的操作test pwd 
/Users/darcy/test
➜  test touch test.txt # 创建文件test vim test.txt # 修改文件test rm test.txt # 删除文件
# 得到的日志输出
创建了一个文件:test.txt
创建了一个文件:.test.txt.swp
修改了一个文件:test.txt
删除了一个文件:.test.txt.swp
删除了一个文件:test.txt

Due to the use of vim editing, temporary swp file generation and automatic deletion were also detected.

Past series of new Java features:

Final words

The article has been included in Github.com/niumoo/JavaNotes , Star and comments are welcome. There are also some first-line interview sites, the core knowledge that Java programmers need to master and other articles, and a lot of my text has been sorted out. Welcome to Star and improve it, and hope that we will become excellent together.

If the article is helpful, you can click " Like " or " Share ", it is all support, I like it all!
Articles are continuously updated every week. To follow my updated articles and shared dry goods in real time, you can follow the " Unread Code " official account or my blog .

No public

Guess you like

Origin blog.csdn.net/u013735734/article/details/108839349