2016/12/10追記 1.8で追加された主としてStreamを使うメソッド群を追加。jdk1.8.0_111を使用。
Java技術最前線 - Java SE 7徹底理解 第4回 New I/O 2の新しいファイルシステムインタフェース その1:ITproとかJava技術最前線 - Java SE 7徹底理解 第5回 New I/O 2の新しいファイルシステムインタフェース その2:ITproとか読んでjava.nio.file.Files便利そうですねー、ということでFilesのメソッドを一通り使ってみることにした。
このエントリを読む上での注意点として、ファイルを扱うAPIてことで環境によっては動いたり動かなかったりがあるってのと、俺がなんかおかしい事書いてる可能性無きにしもあらずなので詳細はFiles (Java Platform SE 7 )をちゃんと読んでください。
Pathの生成
Filesの各メソッドは、基本的にPathを引数に取る。これはjava.io.Fileの代わりみたいなもんで、下記のように生成する。このエントリでは特に指定しない限り↓みたいな感じでPathを作ってあるものとします。
//FileSystem経由 FileSystem fs = FileSystems.getDefault(); Path path = fs.getPath("foo.txt"); //Paths経由の簡易取得 Path srcDir = Paths.get("src");
ファイルの作成
Path createFile(Path path, FileAttribute... attrs)
Files.createFile(Paths.get("hogehoge.txt")); //第二引数でファイルパーミッション指定できる。 Files.createFile(Paths.get("hogehoge.txt"), PosixFilePermissions.asFileAttribute(EnumSet.of(OWNER_READ, GROUP_READ, OTHERS_READ))); //これは↑と同じ事の別表現。なおwindowsでは例外スローする。 Files.createFile(Paths.get("hogehoge.txt"), PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("r--r--r--")));
ディレクトリの作成
createDirectory(Path dir, FileAttribute... attrs)
Files.createDirectory(Paths.get("hogehoge"));
Path createDirectories(Path dir, FileAttribute... attrs)
//↓の場合、createDirectoryはparentディレクトリがないと例外スローする。 Files.createDirectory(Paths.get("parent", "sub")); //↓の場合、createDirectoriesはparentディレクトリ作ってsubディレクトリも作ってくれる。 Files.createDirectories(Paths.get("parent", "sub"));
リンクの作成
Path createLink(Path link, Path existing)
いわゆるハードリンクの作成
Files.createLink(Paths.get("link"), Paths.get("foo.txt"));
Path createSymbolicLink(Path link, Path target, FileAttribute... attrs)
いわゆるシンボリックリンクの作成。当然、OSがサポートしていなければ例外スローする。
Files.createSymbolicLink(Paths.get("link"), Paths.get("foo.txt"));
テンポラリファイル・ディレクトリの作成
Path createTempFile(String prefix, String suffix, FileAttribute... attrs)
テンポラリファイルの作成。prefix, suffixでファイル名を指定する。prefixにnullを入れると適当な名前、suffixにnullを入れると".tmp"になる。ファイルは、java.io.tmpdirのディレクトリに作られる。
Path createTempFile = Files.createTempFile(null, null);
StandardOpenOption.DELETE_ON_CLOSEとの合わせ技で、closeメソッドが呼ばれた時に削除できる。
Path createTempFile = Files.createTempFile(null, null); try (OutputStream os = Files.newOutputStream(createTempFile, DELETE_ON_CLOSE)) { //... }
Path createTempFile(Path dir, String prefix, String suffix, FileAttribute... attrs)
こちらは作成先ディレクトリが指定できる。
Path dir = fs.getPath(""); Path createTempFile = Files.createTempFile(dir, null, null);
Path createTempDirectory(String prefix, FileAttribute... attrs)
テンポラリディレクトリの作成。ファイルの方とほぼ同じ使い方。
Files.createTempDirectory(null);
ディレクトリなのでDELETE_ON_CLOSEで開けないので、javadocによると自動削除はshutdown-hookかFile.deleteOnExit()を使ってね、と書いてある。*1
Path createTempDirectory(Path dir, String prefix, FileAttribute... attrs)
こちらは作成先ディレクトリが指定できる。
Path dir = fs.getPath(""); Path createTempDirectory = Files.createTempDirectory(dir, null);
削除
void delete(Path path)
ファイルorディレクトリの削除。
Files.delete(delPath);
引数で指定したファイルが存在しないと例外をスローする。引数のPathがディレクトリのとき、中身が空無いと例外をスローする。
boolean deleteIfExists(Path path)
ファイルorディレクトリの削除。引数で指定したファイルが存在しなくても例外をスローしない。削除に成功すればtrue、ファイルが存在しなくて削除に失敗すればfalseを返す。
Files.deleteIfExists(delPath)
移動またはリネーム
Path move(Path source, Path target, CopyOption... options)
//リネーム Path src = Paths.get("new.txt"); Path srcRename = Paths.get("newName.txt"); Files.move(src, srcRename); //javadocでは、同一ディレクトリ内での単なるリネームはこういうサンプルコードになっている。 Files.move(src, src.resolveSibling("newName.txt")); //ファイル移動かつ名前変更も出来る。 Files.move(src, Paths.get("src\\changeName.txt")); //ただし、下記のように、移動させたいディレクトリのPathはそのままではmoveメソッドは使えない。 Files.move(src, Paths.get("src"));//new.txtをsrcというファイル名に変更しようとするため、FileAlreadyExistsExceptionになる。 //下記はjavadocのサンプルコードのぱくりだが、同一ファイル名で別ディレクトリへの移動はこんな感じ。 Path newDir = Paths.get("src"); Files.move(src, newDir.resolve(src.getFileName())); //移動先に同一名ファイルがあっても上書きする場合は、下記のオプションをつける。 Files.move(src, newDir.resolve(src.getFileName()), REPLACE_EXISTING);
存在チェック
boolean exists(Path path, LinkOption... options)
見たまんまのメソッド。シンボリックリンクのとき、その先のファイルまでチェックしない場合はオプションで指定する。LinkOptionはこのメソッド以外にも色んなメソッドで使用可能だが、使い方は同じ。
//link2, link3ともにシンボリックリンク。 //ただし、link3の指す先のファイルは存在しない。 System.out.println(Files.exists(Paths.get("link2")));//true System.out.println(Files.exists(Paths.get("link2"), LinkOption.NOFOLLOW_LINKS));//true System.out.println(Files.exists(Paths.get("link3")));//false System.out.println(Files.exists(Paths.get("link3"), LinkOption.NOFOLLOW_LINKS));//true
boolean notExists(Path path, LinkOption... options)
当然existsの逆の結果になる。
System.out.println(Files.notExists(Paths.get("link2")));//false
属性の取得
getXXX, isXXXで特定の属性の取得、チェックを行えるもの。
FileStore getFileStore(Path path)
引数のパスに対する、おおよそファイルシステムを表現したFileStoreを取得する。javadocによると、FileStoreはファイルシステムも含むファイルが格納されているモノ、って感じらしい(適当)
FileStore fileStore = Files.getFileStore(path); //手元のwindowsだと・・・ System.out.println(fileStore.type());//NTFS System.out.println(fileStore.toString());//(C:) System.out.println(fileStore.supportsFileAttributeView(DosFileAttributeView.class));//true System.out.println(fileStore.supportsFileAttributeView(PosixFileAttributeView.class));//false
UserPrincipal getOwner(Path path, LinkOption... options)
ファイルの所有者取得。
UserPrincipal owner = Files.getOwner(path);
FileTime getLastModifiedTime(Path path, LinkOption... options)
更新日時の取得。
FileTime time = Files.getLastModifiedTime(path);
System.out.println(time.toString());//2013-01-03T06:30:56.56625Z とか
Set getPosixFilePermissions(Path path, LinkOption... options)
基本的にはUNIX系OSで取得可能なバーミッションの取得。
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(Paths.get(".project")); System.out.println(permissions);//[GROUP_READ, OWNER_READ, GROUP_WRITE, OTHERS_READ, OWNER_WRITE] とか
long size(Path path)
ファイルサイズの取得。
System.out.println(Files.size(path));
String probeContentType(Path path)
ファイルの属性とはちと違うのかもしれないが。ファイルのMIMEタイプを取得する。probeとか言うくらいだからファイルの中身を調べてるんでしょうね。
System.out.println(Files.probeContentType(path));//text/plain System.out.println(Files.probeContentType(srcDir));//null(※ディレクトリ) System.out.println(Files.probeContentType(Paths.get("pom.xml")));//text/xml System.out.println(Files.probeContentType(Paths.get("hoge.htm")));//text/html System.out.println(Files.probeContentType(Paths.get("hoge_files\\db2sqldev.jpg")));//image/jpeg
boolean isExecutable(Path path)
JVMがRuntime.execできるかどうかをチェックする。UNIX系OSでは実行権限の有無を見る。他のシステムだと、ACLだったりなんだったりと実装依存になるらしい。
System.out.println(Files.isExecutable(Paths.get(".project")));;//-rw-rw-r-- なファイルなので false になる
boolean isHidden(Path path)
隠しファイルかのチェック。隠しファイルの定義はOS依存。javadocによると、UNIX系ではファイル先頭に"."があるかどうか、windowsでは隠しファイル属性がセットされているかどうか、を見るとのこと。
//Ubuntuでの実行例 System.out.println(Files.isHidden(Paths.get("hoge.txt")));//false System.out.println(Files.isHidden(Paths.get(".project")));//true
boolean isRegularFile(Path path, LinkOption... options)
regular fileてのはファイルシステムの種類と機能 − @IT自分戦略研究所あたりを参照ってとこでしょうか。
//フツウのファイル true System.out.println(Files.isRegularFile((Paths.get("aaa.txt")))); //シンボリックリンク、ただしその先のファイルを見に行くので true System.out.println(Files.isRegularFile((Paths.get("link2")))); //シンボリックリンク、ただしその先のファイルを見に行かないので false System.out.println(Files.isRegularFile((Paths.get("link2")),LinkOption.NOFOLLOW_LINKS)); //デバイスファイル false System.out.println(Files.isRegularFile((Paths.get("/dev/bus/usb/001"))));
boolean isSymbolicLink(Path path)
シンボリックリンクかどうかのチェック。
System.out.println(Files.isSymbolicLink((Paths.get("aaa.txt"))));//false System.out.println(Files.isSymbolicLink((Paths.get("link2"))));//true
Windows XPでlinkd.exeで作ったディレクトリへのリンクとか、fsutilで作ったファイルへのハードリンクとかは、falseが返ってきました。
boolean isSameFile(Path path, Path path2)
同一ファイルかのチェック。
//link2 -> hoge.txt のとき、下記はtrueを返す。 System.out.println(Files.isSameFile(Paths.get("link2"),Paths.get("hoge.txt")));
属性の取得(汎用)
isなんちゃらとgetなんちゃらで取れない属性を使いたい場合、こっちを頼るのだと思われる。
Object getAttribute(Path path, String attribute, LinkOption... options)
[view-name:]attribute-name という形式でファイルの属性を取得する。当然、OSやファイルシステムによって指定可能な引数は変わってくる。
//windowsでの実行例 System.out.println(Files.getAttribute(path, "creationTime"));//2013-01-06T01:50:39.477375Z とか返ってくる System.out.println(Files.getAttribute(path, "dos:hidden"));//隠しファイルなら、trueとか返ってくる System.out.println(Files.getAttribute(path, "dos:readonly"));//読み取り専用なら、trueとか返ってくる
<V extends FileAttributeView> V getFileAttributeView(Path path, Class type, LinkOption... options)
FileAttributeView (Java Platform SE 7 )のサブインタフェースを渡すと、それに応じたファイル属性のビューを保持するインスタンスを返してくれる。
//これはwindowsでしか動かない…と思いきやUbuntuでも動いた。 //windowsではsun.nio.fs.WindowsFileAttributeViews、Ubuntuではsun.nio.fs.LinuxDosFileAttributeViewが返ってきた。 //各環境に対応する実装クラスが動作時に存在すれば動く、ってことなんでしょうね。 DosFileAttributeView dosFileAttributeView = Files.getFileAttributeView(path, DosFileAttributeView.class); DosFileAttributes dosFile = dosFileAttributeView.readAttributes(); System.out.println(dosFile.isReadOnly() + ":" + dosFile.isHidden()); //こちらはUbuntuでしか動かない。windowsではposixFileAttributeViewがnullになる。 PosixFileAttributeView posixFileAttributeView = Files.getFileAttributeView(Paths.get("hoge.txt"), PosixFileAttributeView.class); PosixFileAttributes posixFile = posixFileAttributeView.readAttributes(); System.out.println(posixFile.permissions());
<A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)
BasicFileAttributes (Java Platform SE 7 ) のサブインタフェースを渡すと、それに応じたファイル属性を保持するインスタンスを返してくれる。
//これは大抵のOS,ファイルシステムで動くと思われる。 BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class); System.out.println(attributes.creationTime() + " " + attributes.lastAccessTime()); //これもwindows限定かと思いきやUbuntuでも動くことは動く。まともな値を返してるのかは分からないが…… DosFileAttributes attributes = Files.readAttributes(path, DosFileAttributes.class); System.out.println(attributes.isHidden() + " " + attributes.isReadOnly()); //これはwindowsでは例外スローする。 PosixFileAttributes attributes = Files.readAttributes(path, PosixFileAttributes.class); System.out.println(attributes.permissions());
Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)
[view-name:]attribute-listという形式で取れる属性をぜんぶ取ってくる。attribute-listに指定できるリストは、java.nio.file.attribute.AttributeView下のサブインタフェース、たとえばjava.nio.file.attribute.BasicFileAttributeViewとかjava.nio.file.attribute.PosixFileAttributeViewとか。view-nameに使える値はjava.nio.file.attribute.AttributeView.name()てことだけど、環境依存なわけで、その環境で使用可能な値一覧の取り方とかあるんですかね。
//使い方はだいたいこんな感じ。 Files.readAttributes(path, "*")); Files.readAttributes(path, "dos:*"); Files.readAttributes(path, "unix:*"); Files.readAttributes(path, "unix:ino");
属性の設定
Path setLastModifiedTime(Path path, FileTime time)
ファイルの更新日時を変更する。
//現在日時に更新
Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis()));
Path setOwner(Path path, UserPrincipal owner)
下記のコードはwindowsで動かしてるんですが、以前DB2の試用版をインストールしたときにdb2adminユーザが作られてたので、ファイルの所有者をソレに変えています。
UserPrincipalLookupService service = fs.getUserPrincipalLookupService();
UserPrincipal db2admin = service.lookupPrincipalByName("db2admin");
Files.setOwner(path, db2admin);
Path setPosixFilePermissions(Path path, Set perms)
//文字列形式でのパーミッション指定 Files.setPosixFilePermissions(Paths.get("hoge.txt"), PosixFilePermissions.fromString("r--r--r--")); //PosixFilePermissionのEnumのセットで指定。 Files.setPosixFilePermissions(Paths.get("hoge.txt"), EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE)); //PosixFilePermissionの指定(パーミッション全部つける) Files.setPosixFilePermissions(Paths.get("hoge.txt"), EnumSet.allOf(PosixFilePermission.class));
属性の設定(汎用)
Path setAttribute(Path path, String attribute, Object value, LinkOption... options)
ファイル属性の設定。属性を[view-name:]attribute-nameの形式で指定して変更できる。
Files.setAttribute(path, "dos:hidden", true);//隠しファイル属性をtrueにする。
コピー
copy(Path source, Path target, CopyOption... options)
ファイルコピーがカンタンにできるようになったよ! やったね!
Files.copy(path, dest); //コピー先に同名がいても上書きコピー(このオプション指定がないと例外スローする) Files.copy(path, dest, REPLACE_EXISTING); //ATOMIC_MOVEは移動専用(Files.moveで使える)なのでこれは例外スローする Files.copy(path, dest, ATOMIC_MOVE); //アクセス権限とかその辺もコピーされる Files.copy(path, dest, COPY_ATTRIBUTES);
long copy(Path source, OutputStream out)
コピー先をOutputStreamを指定して書き出す。
Path dest = fs.getPath("src", "foo.txt"); try (OutputStream out = Files.newOutputStream(dest)) { Files.copy(path, out); }
long copy(InputStream in, Path target, CopyOption... options)
コピー元をInputStreamを指定して書き出す。
Path dest = fs.getPath("src", "foo.txt"); try (InputStream in = Files.newInputStream(path)) { Files.copy(in, dest); }
java.ioのストリーム生成
InputStream newInputStream(Path path, OpenOption... options)
PathからInputStreamの生成。try-with-finallyとの合わせ技で、このテの処理が書きやすくなる感じ。
try (InputStream is = Files.newInputStream(path);) {
is.read();
}
OpenOptionについては後述。
OutputStream newOutputStream(Path path, OpenOption... options)
とりあえずの使い方。
try (OutputStream out = Files.newOutputStream(outPath);) { //... }
OpenOptionは、ストリームなどをオープンするときのモードを指定する。newOutputStreamのパラメータで省略したとき、CREATE, TRUNCATE_EXISTING, WRITE がデフォで指定される。CREATEは、もしファイルが無ければ作成する。TRUNCATE_EXISTINGは、もしファイルが存在すればサイズ0に切り詰める。WRITEは、書き込み権限アリでファイルを開く。なので、存在しているファイルをなんも考えずにOpenOption指定なしでnewOutputStreamすると突然のサイズ0ファイルが出来上がりややビビる。
下記は、ファイル作成&ファイル末尾への追記モードで開く。
try (OutputStream out = Files.newOutputStream(outPath, CREATE, APPEND);) { byte[] b = {'h', 'o', 'g', 'e'}; out.write(b); }
BufferedReader newBufferedReader(Path path)
1.8で追加。
BufferedReader newBufferedReader(Path path, Charset cs)
BufferedReaderが直で生成できる地味に有り難いメソッド。第二引数のCharSetが無い方はFiles.newBufferedReader(path, StandardCharsets.UTF_8)
と同等。
try (BufferedReader br = Files.newBufferedReader(path, Charset.defaultCharset())) { //... }
BufferedWriter newBufferedWriter(Path path, OpenOption... options)
1.8で追加
BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options)
Charsetが無い方はFiles.newBufferedWriter(path, StandardCharsets.UTF_8, options)
と同等。
try (BufferedWriter bw = Files.newBufferedWriter(outPath, Charset.defaultCharset(), CREATE, APPEND)) { bw.write("hoge"); }
ByteChannelの生成
SeekableByteChannel newByteChannel(Path path, OpenOption... options)
try (SeekableByteChannel channel = Files.newByteChannel(path)) { ByteBuffer buf = ByteBuffer.allocate((int)channel.size()); channel.read(buf); String s = new String(buf.array()); System.out.print(s); }
SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs)
Set<PosixFilePermission> permissionString = PosixFilePermissions.fromString("rw-rw-r--"); FileAttribute<Set<PosixFilePermission>> permissionAttribute = PosixFilePermissions.asFileAttribute(permissionString); try (SeekableByteChannel channel = Files.newByteChannel( Paths.get("newbytechannel.txt"), EnumSet.of(CREATE_NEW,READ,WRITE), permissionAttribute)) { //... }
ディレクトリツリーの探索
Stream list(Path dir)
1.8で追加。
ディレクトリ内のエントリを取得するStreamを返す。再帰的な処理はしない。以下のように書くとdirのパス下のエントリを表示できる。
Files.list(dir).forEach(System.out::println);
Stream walk(Path start, int maxDepth, FileVisitOption... options)
1.8で追加。
細かいことはともかく、こう書くとあるディレクトリ下のファイルとサブディレクトリをすべて表示できる。
Files.walk(dir, Integer.MAX_VALUE).forEach(System.out::println);
Stream find(Path start, int maxDepth, BiPredicate matcher, FileVisitOption... options)
1.8で追加。
あるディレクトリ下で条件に合致するPathから成るStreaemを返す。たとえば、以下のように書くとファイル名が.pngで終わるパスを表示できる。
Files.find(dir, Integer.MAX_VALUE, (p, attr) -> p.toFile().getName().endsWith(".png")).forEach(System.out::println);
Path walkFileTree(Path start, FileVisitor visitor)
細かいことはともかく、こう書くとあるディレクトリ下のファイルをすべて表示できる。下記のコードだと、ディレクトリ下のサブディレクトリの名前は表示されない。なので、このメソッドでの探索だとファイルかディレクトリかのifチェックは不要になる。
FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("Visit File: " + file); return FileVisitResult.CONTINUE; } }; Files.walkFileTree(srcDir, visitor);
このメソッドがどういう動作するのかはJava SE 7徹底理解 ファイルツリーの探索の図を見たほうがわかりやすいです。
ディレクトリの探索
下記の一連のメソッドは、引数で指定されたディレクトリ内のすべてのファイルとディレクトリなどのエントリを走査する。
DirectoryStream newDirectoryStream(Path dir)
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get("src"))) { for (Path path : directoryStream) { System.out.println(path.toAbsolutePath()); } }
DirectoryStream newDirectoryStream(Path dir, String glob)
あるパターンにマッチしたエントリだけを対象にする。globに指定可能なパターンはgetPathMatcherを参照。
Files.newDirectoryStream(Paths.get(""), "*.{txt,exe}");// 名前が .txt か .exe で終わるもの。
DirectoryStream newDirectoryStream(Path dir, DirectoryStream.Filter filter)
フィルターを適用してからイテレータを返してくれる感じのメソッド。Rubyとかでは良く見かけるやり方だけどJavaではちと長々しくなりますなλ...
//javadocからパクって来たコード。ファイルサイズが10より大きいものだけ処理対象にする。 DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { public boolean accept(Path file) throws IOException { return (Files.size(file) > 10); } }; try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(""), filter)) { for (Path path : directoryStream) { System.out.println(path.toAbsolutePath()); } }
読み込み
簡易的にファイルへ読み込み・書き込みが出来るメソッド。
Stream lines(Path path)
Stream lines(Path path, Charset cs)
1.8で追加。readAllLinesとは異なりStreamを返す。以下のようにするとファイルの中身を表示できる。Charsetが無い方はFiles.lines(path, StandardCharsets.UTF_8)
と同等。
Files.lines(path).forEach(System.out::println);
byte[] readAllBytes(Path path)
引数で指定されたファイルをぜんぶ読んでbyte配列で返す、という漢気を感じさせるメソッド。
byte[] bytes = Files.readAllBytes(path);
List readAllLines(Path path)
1.8で追加。
List readAllLines(Path path, Charset cs)
こちらはファイル内の全行をStringのListで返す、というメソッド。Charsetが無い方はFiles.readAllLines(path, StandardCharsets.UTF_8)
と同等。
for (String string : Files.readAllLines(path, Charset.defaultCharset())) {
System.out.println(string);
}
書き込み
Path write(Path path, byte[] bytes, OpenOption... options)
//一応↓でファイルコピー的なことが出来る。 Files.write(Paths.get("writeTo.txt"), Files.readAllBytes(path));
Path write(Path path, Iterable lines, OpenOption... options)
1.8で追加。
Path write(Path path, Iterable lines, Charset cs, OpenOption... options)
Charsetが無い方はFiles.write(path, lines, StandardCharsets.UTF_8, options);
と同等。
//一応↓でファイルコピー的なことが出来る。 Files.write(Paths.get("writeTo.txt"), Files.readAllLines(path, Charset.defaultCharset()), Charset.defaultCharset());
その他
Path readSymbolicLink(Path link)
シンボリックリンクの先のファイルのPathを取得する。シンボリックリンクでないファイルは例外スローする。また、リンクの先のファイルが存在してるかどうかはチェックしない。シンボリックリンクの無いwindowsでは例外スローする。
//link2 > hoge.txt link3 > hogehoge.txt ただしhogehoge.txtは存在しない。 Path link2Path = Files.readSymbolicLink(Paths.get("link2")); Path link3Path = Files.readSymbolicLink(Paths.get("link3")); System.out.println(link2Path.toAbsolutePath());//hoge.txt を返す。 System.out.println(link3Path.toAbsolutePath());//hogehoge.txt を返す。
*1:むかし、File.deleteOnExitは環境依存でファイルが消えない時があるとかなんとかあったと思うんですがそれはどうなったんですかねぇ……