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;
}
}
代码解释:
- 注册目录和事件:
dir.register(watcher, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE)注册了目录dir,并指定了我们关注的事件类型——文件的创建、修改和删除。 - 事件处理:程序使用
watcher.take()阻塞等待事件。每当发生感兴趣的事件时,signaledKey被唤醒并处理事件。 - 文件名获取:我们通过
event.context()获取与事件相关的文件名,然后打印到控制台。 - 重置 WatchKey:
signaledKey.reset()是必须的,它保证监控能够继续进行。如果reset()返回false,表示监控的目录已不可用,程序退出。
总结:
Watch Service API 非常适合需要实时监控文件变化的应用,如文本编辑器、IDE 和自动部署系统。它允许你轻松地处理文件创建、修改和删除等事件,并且提供了灵活的方式来响应这些事件。尽管它也可以用于文件系统轮询,但如果文件系统本身支持变更通知,Watch Service API 可以显著提高效率。在使用时要注意类型转换的问题,并使用合适的抑制警告机制。