Java IO提供了File.deleteOnExit()方法,该方法会在JVM正常结束时删除调用它的文件。我发现这对于清理临时文件特别是在单元测试期间非常有用。
然而,在Java NIO的Files类中并没有同名方法。我知道可以使用path.toFile().deleteOnExit()
来实现相同的功能,但我想知道是否有使用NIO的另一种方法。
有替代方法吗?如果没有,为什么没有呢?
Java IO提供了File.deleteOnExit()方法,该方法会在JVM正常结束时删除调用它的文件。我发现这对于清理临时文件特别是在单元测试期间非常有用。
然而,在Java NIO的Files类中并没有同名方法。我知道可以使用path.toFile().deleteOnExit()
来实现相同的功能,但我想知道是否有使用NIO的另一种方法。
有替代方法吗?如果没有,为什么没有呢?
在Java NIO中,您无法删除任意文件,但是可以在打开新流时使用StandardOpenOption.DELETE_ON_CLOSE
参数,这将在流关闭时(包括从try-with-resources语句中调用.close()
或JVM终止)立即删除文件。例如:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
经过大量探索,我发现Java NIO确实有一种删除文件的方法,但它采用了与Java I/O不同的方式。
首先,Files.createTempFile()
的Javadoc描述了三种删除文件的方式:
如果作为工作文件,可以使用
DELETE_ON_CLOSE
选项打开生成的文件,以便在调用适当的关闭方法时删除该文件。或者,可以使用shutdown hook或File.deleteOnExit()
机制自动删除文件。
最后一个选择File.deleteOnExit()
显然是Java I/O方法,我们要避免使用它。当您调用上述方法时,后台发生的是Shutdown-hook。但DELETE_ON_CLOSE
选项是纯Java NIO。
Java NIO假设您只想删除实际打开的文件,而不是任意文件。因此,创建新流的方法(例如Files.newOutputStream()
)可以可选地采用多个OpenOptions
,在其中您可以输入StandardOpenOption.DELETE_ON_CLOSE
。那样会在流关闭时(通过调用.close()
或JVM退出)立即删除该文件。
例如:
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
当流被关闭时,无论是由于显式调用.close()
、在try-with-resources语句中作为一部分关闭,还是由JVM终止,都将删除与该流关联的文件。
更新:在某些操作系统(例如Linux)上,StandardOpenOption.DELETE_ON_CLOSE
会在创建OutputStream时立即删除。如果您只需要一个OutputStream,那可能仍然可以使用。有关详细信息,请参见DELETE_ON_CLOSE deletes files before close on Linux。
因此,Java NIO比Java I/O增加了新功能,您可以在关闭流时删除文件。如果这足以替代在JVM退出期间删除文件,则可以使用纯Java NIO实现。否则,您将不得不依赖于Java I/O的File.deleteOnExit()
或shutdown-hook来删除文件。
DELETE_ON_CLOSE
不能完全取代File.deleteOnExit()
,因为它要求你必须保持输出流打开状态才能与文件交互。而且,在一些平台上,文件实际上会在关闭前立即被删除。 - daiscogDELETE_ON_CLOSE
的工作方式不同,它会在“流关闭时立即删除文件”,因此我认为我已经清楚地表明它的工作方式与 File.deleteOnExit()
不同。如果你可以接受它立即删除(在我的用例中,我是可以接受的),那么这是一个很好的替代方案。 - Thunderforge在幕后,File.deleteOnExit()
只会通过Runtime.addShutdownHook()
创建一个shutdown hook
。
然后,您可以使用NIO执行相同的操作:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
Path path = ...;
Files.delete(path);
}
});
StandardOpenOption.DELETE_ON_CLOSE
硬塞到File.deleteOnExit()
的替代方案中。正如文档所述,它既不是通用的,也不太可能在非琐碎情况下正常工作。DELETE_ON_CLOSE
旨在在文件关闭后立即清理不再需要的资源,因此适用于删除文件。Files.createTempFile()
的文档也明确指出,DELETE_ON_CLOSE
仅适用于文件打开时才需要的“工作文件”。
Files.createTempFile()
文档建议直接编写自己的关闭挂钩程序,或者继续使用File.deleteOnExit()
。如果你只是在使用本地文件系统,那么使用File.deleteOnExit()
并没有任何本质上的问题,尽管你希望使用NIO。如果你没有使用(或者不确定是否使用)本地文件系统,因此无法使用File.deleteOnExit()
,那么编写自己的关闭挂钩程序就足够简单了,就像File
一样:
public final class DeletePathsAtShutdown {
private static LinkedHashSet<Path> files = new LinkedHashSet<>();
static {
Runtime.getRuntime().addShutdownHook(
new Thread(DeletePathsAtShutdown::shutdownHook));
}
private static void shutdownHook() {
LinkedHashSet<Path> local;
synchronized {
local = paths;
paths = null;
}
ArrayList<Path> toBeDeleted = new ArrayList<>(theFiles);
Collections.reverse(toBeDeleted);
for (Path p : toBeDeleted) {
try {
Files.delete(p);
} catch (IOException | RuntimeException e) {
// do nothing - best-effort
}
}
}
public static synchronized void register(Path p) {
if (paths == null) {
throw new IllegalStateException("ShutdownHook already in progress.");
}
paths.add(p);
}
}
DeletePathsAtShutdown
添加更多功能,例如remove()
函数或删除目录的支持。
.toFile()
之外。) - Peter Lawrey@Deprecated
了,但它仍然存在。“完全生活在NIO中”的好处是什么? - Peter Lawrey