389. Java IO API - 获取文件名

0 阅读4分钟

389. Java IO API - 获取文件名

在处理文件系统事件时,我们可以通过事件上下文(context)来获取文件名。在上面的例子中,获取文件名的代码如下:

WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path filename = ev.context();

当你编译这个示例时,可能会看到类似以下的警告:

Note: Example.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

这个警告是由于代码中的类型转换所导致的:WatchEvent<?> 被强制转换为 WatchEvent<Path>。这属于类型安全问题,因此编译器发出了警告。为了避免这个警告,你可以使用一个辅助的转换方法来抑制未检查的警告,代码如下:

@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
    return (WatchEvent<Path>) event;
}

在这个方法中,使用了 @SuppressWarnings("unchecked") 注解来告诉编译器忽略这个类型转换的警告。该注解通常用于抑制编译器的警告信息,在某些情况下,程序员清楚自己在做什么,且不需要修改现有代码来解决警告。

何时使用与不使用这个 API

Watch Service API 主要适用于那些需要实时接收文件变更通知的应用程序。它非常适合以下场景:

  • 文本编辑器或 IDE(集成开发环境):这些应用程序可能会有多个文件处于打开状态,并且需要确保这些文件与文件系统保持同步。当文件发生变化(如修改、删除或创建)时,应用程序可以立即得知。
  • 应用服务器:例如,应用服务器可以监视某个目录,当 .jsp.jar 文件被添加到该目录时,服务器能够自动部署它们。

然而,这个 API 并不适用于硬盘索引等场景。大多数文件系统实现原生支持文件变更通知,Watch Service API 便是利用了这一点。然而,如果文件系统本身不支持变更通知机制,Watch Service 会采用轮询的方式等待事件,这时它会以一种较低效的方式检测文件系统的变化。

示例扩展

假设我们有一个目录监控系统,它用来监视 .txt 文件的变化,并在文件创建时打印文件名。我们可以将之前提到的代码与上面的解释结合,来实现一个简单的文件监控系统:

import java.io.IOException;
import java.nio.file.*;

import static java.nio.file.StandardWatchEventKinds.*;

public class FileWatcher {
    public static void main(String[] args) {
        Path dir = Paths.get("path/to/your/directory"); // 监控的目录
        try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
            // 注册目录和感兴趣的事件
            WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
            
            // 持续监控目录变化
            for (;;) {
                WatchKey signaledKey;
                try {
                    signaledKey = watcher.take(); // 等待事件
                } catch (InterruptedException e) {
                    return; // 如果线程被中断,退出程序
                }
                
                // 处理事件
                for (WatchEvent<?> event : signaledKey.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();
                    if (kind == OVERFLOW) {
                        continue; // 如果发生溢出事件,跳过
                    }
                    
                    // 获取文件名
                    WatchEvent<Path> ev = cast(event);
                    Path filename = ev.context();
                    System.out.println("文件变化: " + filename);
                    
                    // 在此处理文件(如:判断文件类型等)
                }
                
                // 重置 WatchKey,准备接收新的事件
                boolean valid = signaledKey.reset();
                if (!valid) {
                    break; // 如果 WatchKey 无效,退出循环
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    static <T> WatchEvent<T> cast(WatchEvent<?> event) {
        return (WatchEvent<T>) event;
    }
}

代码解释:

  1. 注册目录和事件dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE) 注册了目录 dir,并指定了我们关注的事件类型——文件的创建、修改和删除。
  2. 事件处理:程序使用 watcher.take() 阻塞等待事件。每当发生感兴趣的事件时,signaledKey 被唤醒并处理事件。
  3. 文件名获取:我们通过 event.context() 获取与事件相关的文件名,然后打印到控制台。
  4. 重置 WatchKeysignaledKey.reset() 是必须的,它保证监控能够继续进行。如果 reset() 返回 false,表示监控的目录已不可用,程序退出。

总结:

Watch Service API 非常适合需要实时监控文件变化的应用,如文本编辑器、IDE 和自动部署系统。它允许你轻松地处理文件创建、修改和删除等事件,并且提供了灵活的方式来响应这些事件。尽管它也可以用于文件系统轮询,但如果文件系统本身支持变更通知,Watch Service API 可以显著提高效率。在使用时要注意类型转换的问题,并使用合适的抑制警告机制。