こんにちは、さるまりんです。
前に「Javaでテキストファイルを読むだけ」の記事を書いたのですが(懐かしい〜)、今回はその続編です。
今回は NIO.2(New I/O 2) を触ってみました。
Java 7 で登場したわりには、いまだに “知らないと損する便利API” みたいな扱いを受けている領域です。
ざっくり言うと:
Path… パスを“文字列じゃなくてオブジェクト”として扱えるFiles… 「コピー」「移動」「読み書き」「探索」などを一発でWatchService… フォルダの変更を“見張れる”- 安全な I/O の書き方が揃っている
File API より直感的で、OS差も吸収してくれるので、覚えるとかなり便利!
今回の記事では、まず “ちょっと本気寄りのファイル操作” を 5 つ、ミニ実験してみました。
1. SafeWrite — 一時ファイル → アトミック移動で安全に書く
「書きかけのファイルが壊れたら困る!」
そんなときは テンポラリに書いてから、本番ファイルへ一瞬で差し替えます。
// SafeWrite.java
import java.nio.file.*;
public class SafeWrite {
public static void main(String[] args) throws Exception {
Path temp = Paths.get("tmp-data.txt");
Path real = Paths.get("data.txt");
Files.writeString(temp, "Hello from safe write!\n");
Files.move(temp, real, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
System.out.println("safe write done -> " + real.toAbsolutePath());
}
}
ポイント:
ATOMIC_MOVEが “一瞬で入れ替え” の主役- ログ・設定ファイル更新・バッチ出力など、壊れてほしくない場面で本領発揮
2. WalkAndMatch — ディレクトリを歩きながらパターン絞り込み
Files.walk() でディレクトリを再帰で歩きつつ、
PathMatcher で glob と 正規表現 の両方を使ってパターンマッチできます。
// WalkAndMatch.java
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;
public class WalkAndMatch {
public static void main(String[] args) throws IOException {
Path root = Paths.get("data");
PathMatcher glob = FileSystems.getDefault().getPathMatcher("glob:**/*.log");
PathMatcher regex = FileSystems.getDefault().getPathMatcher("regex:.*/app-\\d{4}-\\d{2}-\\d{2}\\.log");
try (Stream<Path> s = Files.walk(root)) {
s.filter(Files::isRegularFile)
.filter(p -> glob.matches(p) || regex.matches(p))
.forEach(System.out::println);
}
}
}
ポイント:
"glob:**/*.log"で “どこにあるログでも拾う”"regex:..."を使えば複雑なパターンにも対応- 再帰探索も勝手にやってくれるので、手書きの再帰とはお別れです
3. WatchDir — フォルダの“変化”をリアルタイム監視
WatchService を使うと、
ファイルの追加・更新・削除をリアルタイムで監視できます。
// WatchDir.java
import java.nio.file.*;
public class WatchDir {
public static void main(String[] args) throws Exception {
Path dir = Paths.get("data");
WatchService watcher = FileSystems.getDefault().newWatchService();
dir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE
);
System.out.println("watching: " + dir.toAbsolutePath());
for (;;) {
WatchKey key = watcher.take();
for (WatchEvent<?> ev : key.pollEvents()) {
System.out.println("event: " + ev.kind() + " -> " + ev.context());
}
key.reset();
}
}
}
ポイント:
- Mac / Linux / Windows 共通で動く
- ログディレクトリを覗いたり、ファイルの到着待ちなどに使える
- 一度動かしてみると“めちゃ楽しい”系API
4. CopyAttrs — 属性や権限を保ったままコピー
ただ内容をコピーするだけでなく、
パーミッション・タイムスタンプも維持したままコピーできます。
// CopyAttrs.java
import java.nio.file.*;
public class CopyAttrs {
public static void main(String[] args) throws Exception {
Path src = Paths.get("sample.txt");
Path dst = Paths.get("copied.txt");
Files.copy(src, dst,
StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING
);
System.out.println("copied with attrs -> " + dst.toAbsolutePath());
}
}
ポイント:
- バックアップ・世代管理などで便利
COPY_ATTRIBUTESを足すだけでOK
5. LineStream — 行単位ストリーム処理のおまけ
行ストリームで “読みながら加工して出力” できます。
// LineStream.java
import java.nio.file.*;
import java.util.stream.*;
public class LineStream {
public static void main(String[] args) throws Exception {
Path p = Paths.get("sample.txt");
try (Stream<String> lines = Files.lines(p)) {
lines.filter(l -> !l.isBlank())
.map(String::toUpperCase)
.forEach(System.out::println);
}
}
}
ポイント:
- フィルタ → 変換 → 出力の流れが自然
- 軽いログ処理に重宝する
おわりに:NIO.2、奥が深い
軽く触っただけでも「お〜便利だな〜」となりますが、
NIO.2 は 非同期 I/O(AsyncFileChannel) や メモリマップドファイル など、まだまだ深掘りポイントがたくさんあります。
この先も、さるまりんのガレージで、
「もうちょっとプロっぽい I/O の書き方」を一緒に実験していければと思います。
読んでくださってありがとうございました。
それではまた!