Java IO=には File.deleteOnExit() があります。これは、JVMの正常終了中に呼び出されたファイルを削除するメソッドです。特に単体テスト中の一時ファイルのクリーンアップに非常に役立ちます。
ただし、Java NIOの Files クラスに同じ名前のメソッドが表示されません。path.toFile().deleteOnExit()
を実行できることを認識しています。しかし、NIOを使用する代替手段があるかどうかを知りたいです。
代替手段はありますか?そうでない場合、なぜないのですか?
Java NIOで任意のファイルを削除することはできませんが、新しいストリームを開くときに StandardOpenOption.DELETE_ON_CLOSE
を使用できます。 .close()
の呼び出し(try-with-resourcesステートメントからの呼び出しを含む)またはJVMの終了により、ストリームが閉じます。
Files.newOutputStream(Paths.get("Foo.tmp"), StandardOpenOption.DELETE_ON_CLOSE);
たくさん掘り下げた後、Java NIOdoesが終了時に削除する方法を持っていることがわかりましたが、これは、Java I/O。
まず、 Files.createTempFile()
のJavadocは、ファイルを削除する3つの方法を説明しています。
work files[sic]として使用される場合、結果ファイルは
DELETE_ON_CLOSE
オプションを使用して開くことができます。適切なcloseメソッドが呼び出されたときにファイルが削除されること。または、 shutdown-hook 、またはFile.deleteOnExit()
メカニズムを使用して、ファイルを自動的に削除することもできます。
最後の選択肢であるFile.deleteOnExit()
は、もちろんJava I/Oメソッドです。これは回避しようとしています。シャットダウンフックは、呼び出し時に舞台裏で行われていることです。前述の方法ですが、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終了のいずれかによって、ストリームに関連付けられたファイルを削除します。
Update:Linuxなどの一部のオペレーティングシステムでは、OutputStreamが作成されるとすぐにStandardOpenOption.DELETE_ON_CLOSE
が削除されます。必要なものが1つのOutputStreamだけであれば、それでも大丈夫かもしれません。詳細については、 DELETE_ON_CLOSEはLinuxで閉じる前にファイルを削除します を参照してください。
したがって、Java NIOは、Java I/Oに新しい機能を追加します。これにより、ストリームを閉じるときにファイルを削除できます。 JVM出口は、純粋なJava NIOで実行できます。そうでない場合は、Java I/OのFile.deleteOnExit()
に依存する必要がありますまたは、ファイルを削除するためのシャットダウンフック。
舞台裏では、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()
を使用し続けることを提案しています。 NIOを使用したいという希望にもかかわらず、ローカルファイルシステムのみで作業している場合、File.deleteOnExit()
を使用しても本質的に問題はありません。ローカルファイルシステムを使用していない(または使用していることが確実でない)場合、ca n'tは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);
}
}
もちろん、NIOがすぐに同様のシャットダウンフックを備えていればいいかもしれませんが、その不在は、ジョブに間違ったツールを使用する理由にはなりません。 remove()
関数などの機能をDeletePathsAtShutdown
に追加したり、 ディレクトリの削除 をサポートしたりすることもできます。