javaのNIO.2おさらい

javaのIO,NIOに続いて、NIO.2についておさらい！！

IO : https://qiita.com/liguofeng29/items/e08dc21b16c0057f601e
NIO : https://qiita.com/liguofeng29/items/c827af3d62f219e17755

# NIO.2とは
簡単に言えば、既存のNIOを改善したものです。

1. FileIOを強化した
2. 非同期Channleに基づくIOを強化した

# FileIO強化

## コアAPI
|API名|概要|
|---|---|
| Path | プラットフォームへ依存しない経路を表す|
| Paths | Pathのユーティルクラス<br>(Pathインスタンス取得用(get)メソッドのみだった) | 
| Files | Fileのユーティルクラス | 

雑談：Paths,Filesはjavaの一貫した命名ルールに従ったものですね。

* Paths, Pathのユーティルクラス
* Files, Fileのユーティルクラス__と言いながら引数が殆どPathだった！！__
* Arrays, Arrayのユーティルクラス
* Collections, Collectionのユーティルクラス
* Objects, Objectのユーティルクラス

### Path, Paths
```java:Pathサンプル(markdownで取得したかった)
private static void showPath(Path path) {
    System.out.println("|取得内容|結果|");
    System.out.println("|---|---|");
    System.out.printf("|%s | %s |\r\n", "要素数", path.getNameCount());
    System.out.printf("|%s | %s |\r\n", "指定要素名", path.getName(0));
    System.out.printf("|%s | %s |\r\n", "ファイル名", path.getFileName());
    System.out.printf("|%s | %s |\r\n", "ファイルシステム名", path.getFileSystem());
    System.out.printf("|%s | %s |\r\n", "親", path.getParent());
    System.out.printf("|%s | %s |\r\n", "ルート", path.getRoot());
    System.out.printf("|%s | %s |\r\n", "絶対パスか", path.isAbsolute());
}
```
```java:Path,Paths
private static void path_test() {
    // 相対パス
    Path relativePath = Paths.get(".", "NIO2", "sub", "nio2-file.txt");
    System.out.println("---- 相対パス ----");
    showPath(relativePath);
    // 絶対パス
    System.out.println("---- 絶対パス ----");
    showPath(relativePath.toAbsolutePath());
}
```
* 相対パス 

|取得内容|結果|
|---|---|
|要素数 | 4 |
|指定要素名 | . |
|ファイル名 | nio2-file.txt |
|ファイルシステム名 | sun.nio.fs.WindowsFileSystem@6e0be858 |
|親 | .\NIO2\sub |
|ルート | null |
|絶対パスか | false |

* 絶対パス 

|取得内容|結果|
|---|---|
|要素数 | 8 |
|指定要素名 | workspace |
|ファイル名 | nio2-file.txt |
|ファイルシステム名 | sun.nio.fs.WindowsFileSystem@6e0be858 |
|親 | C:\workspace\git\java-projects\java-sample\.\NIO2\sub |
|ルート | C:\ |
|絶対パスか | true |

1. 絶対パス、相対パスによって取得する結果が異なる
2. sun.nio.fs.WindowsFileSystem、これによって__「プラットフォームへ依存しない」__を実現しているのね？
3. Paths#getからPathインスタンス取得する(__File to Path, Path to Fileもできるが__)

### Files
#### Filesサンプル
```text:nio2-src.txt
１行目、ＡＩＣ
２行目、ＵＴＦ－８？
３行目
```
```java:Filesサンプル(markdownで取得したかった)
private static void testFiles(Path path) {
    System.out.println("|取得内容|結果|");
    System.out.println("|---|---|");
    try {
        System.out.printf("|%s | %s |\r\n", "ディレクトリか", Files.isDirectory(path));
        System.out.printf("|%s | %s |\r\n", "隠しか", Files.isHidden(path));
        System.out.printf("|%s | %s |\r\n", "ディレクトリか", Files.isWritable(path));
        System.out.printf("|%s | %s |\r\n", "サイズは", Files.size(path));
        System.out.printf("|%s | " , "ファイル内容取得");
        Files.lines(path).forEach(line -> {
            System.out.printf("%s<br>" , line);
        });
        System.out.printf("|\r\n");
        System.out.printf("|%s | " , "ディレクトリ要素取得");
        Files.list(path.getParent()).forEach(p -> {
            System.out.printf("%s<br>" , p);
        });
        System.out.printf("|\r\n");
        System.out.printf("|%s | %s |\r\n", "ストレジ容量", Files.getFileStore(path).getTotalSpace() / 1024 / 1024 / 1024 + "GB");
        System.out.printf("|%s | %s |\r\n", "利用可能容量", Files.getFileStore(path).getUsableSpace() / 1024 / 1024 / 1024 + "GB");
        System.out.printf("|%s | %s |\r\n", "未割当て容量", Files.getFileStore(path).getUnallocatedSpace() / 1024 / 1024 / 1024 + "GB");
        System.out.printf("|%s | %s |\r\n", "存在するか", Files.exists(path));
        Files.delete(path);
        System.out.printf("|%s | %s |\r\n", "削除する", "削除した");
        System.out.printf("|%s | %s |\r\n", "存在するか", Files.exists(path));
    } catch (IOException e) {
        e.printStackTrace();
    }
}
```

|取得内容|結果|
|---|---|
|ディレクトリか | false |
|隠しか | false |
|ディレクトリか | true |
|サイズは | 64 |
|ファイル内容取得 | １行目、ＡＩＣ<br>２行目、ＵＴＦ－８？<br>３行目<br>|
|ディレクトリ要素取得 | .\NIO2\nio2-desc.txt<br>.\NIO2\nio2-src.txt<br>.\NIO2\sub<br>|
|ストレジ容量 | 111GB |
|利用可能容量 | 54GB |
|未割当て容量 | 54GB |
|存在するか | true |
|削除する | 削除した |
|存在するか | false |

#### File#WalkTree
既存IOでは、再帰的な走査を自分で実装する必要があり、コードが複雑＆使い勝手がよくなかった。

1.7では、Files#walkFileTreeで再帰的走査が可能になる。

|callback|概要|
|---|---|
| preVisitDirectory | ディレクトリアクセス前発生
| visitFile | ディレクトリアクセス前発生
| visitFileFailed | ディレクトリアクセス前発生
| postVisitDirectory | ディレクトリアクセス後発生

|FileVisitResult|概要|
|---|---|
|CONTINUE| 継続する |
|SKIP_SIBLINGS| 継続後、該当file,directoryの兄弟階層は走査しない |
|SKIP_SUBTREE| 継続後、該当file,directoryの子供階層は走査しない|
|TERMINATE|walk(走査)を終了する |

walkFileTreeのサンプルはあっちこっち在ったので、割愛！

#### Files#walk
1.8では、Files#walkが便利！

```java:Files.walk
Files.walk(Paths.get(".", "NIO2"), FileVisitOption.FOLLOW_LINKS)
    // directoryとfile入れ替え
    .sorted(Comparator.reverseOrder())
    // 削除対象確認
    .peek(System.out::println)
    // 削除
    .forEach(path -> {
        try {
            Files.delete(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    });
```

## ほか
### WatchService 
File変更を監視する仕組みである。
従来のやり方で、考えられる実装は
1. 別スレッドを立ち上げて
2. 状態を保存して
3. 一定間隔で走査して前の状態と比較する

WatchServiceを使えばファイルが簡単にできる。

```java:WatchServiceサンプル
try {
    WatchService watchService = FileSystems.getDefault().newWatchService();
    Paths.get(".").register(
        watchService,
        StandardWatchEventKinds.ENTRY_CREATE, // CREATE監視
        StandardWatchEventKinds.ENTRY_DELETE, // DELETE監視
        StandardWatchEventKinds.ENTRY_MODIFY  // MODIFY監視
    );
    System.out.println("-----watch開始-----");
    while (true) {
        // 待ち続ける
        WatchKey key = watchService.take();
        key.pollEvents().stream()
            .forEach(
                watchEvent -> {
                    System.out.println("発生種類 : " + watchEvent.kind().name());
                    System.out.println("対象名　 : " + watchEvent.context());
                });
        // 次の監視
        if (!key.reset()) {
            break;
        }
    }
} catch (IOException | InterruptedException e) {
    e.printStackTrace();
}
```

1. WatchServiceを生成する
2. WatchServiceを登録し、監視内容も登録する
3. WatchKey取得
    * take() : WatchKey取得するまで待ち続ける
    * poll() : WatchKey取得し、なければnull
    * poll(long timeout, TimeUnit unit):timeout分待ち続ける
4. 処理
5. WatchKeyリセット

### 属性読み書き
ざっくり、FileAttributeViewを実装したもので、細かい属性を操作できるそうです。

- AclFileAttributeView
- BasicFileAttributeView
- DocFileAttributeView
- FileOnwerFileAttributeView
- PosixFileFileAttributeView
- UserDefinedFileAttributeView

```java:BasicFileAttributeViewサンプル
Path path = Paths.get(".", "NIO2", "nio2-src.txt");

BasicFileAttributeView view = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes attributes = view.readAttributes();

System.out.println("前回アクセス : " + attributes.lastAccessTime());
System.out.println("最新修正時刻 : " + attributes.lastModifiedTime());
```