【Java】ストリーム処理

#ストリームとは
* データの流れ
* **入出力先にかかわらずデータの連なりとして同じように操作することができる**
    * 入力（受け取り）/ 出力（書き出し）ストリーム
    * 文字ストリーム / バイナリストリーム

|基本クラス|入力ストリーム|出力ストリーム|
|:-:|:-:|:-:|
|文字ストリーム|Reader|Writer|
|バイナリストリーム|InputStream|OutputStream|

* 基本クラスをもとに操作対象に応じ操作クラスを提供している

#ファイル書き込み
* **ファイルオープン**
    * **BufferedWriterクラス**のnewBufferedWriterメソッドを使う（Bufferは文字列データを一時的に保存するメモリ領域）
    * 引数に`StandardOpenOption.APPEND`指定で追記モード


* **ファイル出力**
    * **writeメソッド**
    * データをBufferに保存して溜まったらまとめてファイル出力
    * **newLineメソッド**で改行区切り(Unixは`\n`,Windowsは`\r\n`)
    * Files.writeメソッドで文字列リストをまとめてファイルに書き込みできる
        * `Files.write(Paths.get("./data.log"),list,StandardCharsets.UTF_8);`


* **ファイルクローズ**
    * `try-with-resources`構文を使う
    * tryで生成されたオブジェクトは**ブロックを抜けたら自動で破棄**される！(途中のエラーでcloseメソッドが呼び出されない場合でも大丈夫)
 
```java
//実行時間を./data.logに記録
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
//import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;

public class Main {
  public static void main(String[] args) {
    try (var writer = Files.newBufferedWriter(Paths.get("./data.log"))) {
    //ファイル追記
    //try (var writer = Files.newBufferedWriter(Paths.get("./data.log"), StandardOpenOption.APPEND)) {
    writer.write(LocalDateTime.now().toString());
    // 改行文字を統一する場合
    //write(LocalDateTime.now().toString()+"\r\n");
      writer.newLine();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
```

#ファイル入力
* **BufferedReaderクラス**のnewBufferedReaderメソッドを使う
* **readLineメソッド**でファイルポインタで1行ずつ読みとり現在行の文字列を返す
    * ファイルポインター：現在位置の目印。ファイルオープン時にファイル先頭にある。次の行がない場合にnullを返す
* **Files.readAllLinesメソッド**では、ファイル内容を行単位でまとめてリスト取得できる。ファイルサイズが大きい場合メモリの消費が激しい

```java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    try (var reader = Files.newBufferedReader(Paths.get("./sample.txt"))) {
      var line = "";
      while ((line = reader.readLine()) != null) {
        //ファイルの中身を
        System.out.println(line);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
```

#バイナリファイルの読み書き
* **BufferedInputStream** / **BufferedOutputStream**
    * バッファー機能を備えたバイトストリーム
    * ファイルへの入出力は**FileInputStream** / **FileOutputStream**
    * ストリームへの読み書きは **read** / **writeメソッド**
        * readの戻り値はバイトデータ(0~255)
        * ストリーム終端は-1を返す

```java
//input.pngをBufferedInputStreamで読み込み
//結果をBufferedOutputStreamでoutput.pngに出力
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    try (
        var in = new BufferedInputStream(
          new FileInputStream("./input.png"));
        var out = new BufferedOutputStream(
          new FileOutputStream("./output.png"))) {
      var data = -1;
      while ((data = in.read()) != -1) {
        out.write((byte) data);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
```

#オブジェクトのシリアライズ
* **シリアライズ**：のような構造化データをバイト配列に変換
* オブジェクトをバイト配列にシリアライズすることでオブジェクトをDBに保存、ネットワーク経由の受け渡し可能
* **シリアライズ可能クラスの条件**
    * Serializableインターフェース実装<br>`public class Article implements Serializable {`
        * 宣言のみでメソッドを持たない=**マーカーインターフェース**
    * インスタンスフィールドは基本型、シリアライズ可能な型
    * シリアライズバーション宣言
        * **serialVersionUID**でクラスのバージョン指定
        * シリアライズ/デシリアライズ間で異なるバージョンのクラスを使用するとInvalidClassException
    * 基底クラスがシリアライズできないとき、引数のないコンストラクタを持つ
        * 基底クラスがSerializableインターフェース実装せず、派生クラスのみシリアライズ可能の時、派生クラスのデシリアライズに基底クラスのコンストラクタを呼ぶ
        
* **ObjectOutputStreamクラス**でオブジェクトのシリアライズを行う
    * 引数に`FileOutputStream(file)`でファイル出力
    * writeObjectメソッドにオブジェクトを渡す（article.ser作成）
* **ObjectInputStreamクラス**でデシリアライズ
    * 引数に読み込むfileを渡しreadObjectメソッドで読み込む
    * 戻り値はオブジェクト

```java
//article.serにArticleクラス(シリアライズ可能クラス)を保存
//ファイルからバイト配列を読みデシリアライズした公開鍵暗号オブジェクトの内容を出力
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Main {
  public static void main(String[] args) {
    final var file = "./article.ser";
    try (var out = new ObjectOutputStream(new FileOutputStream(file))) {
      out.writeObject(new Article("Java11の変更点と新しいAPI",
        "https://codezine.jp/article/corner/751", false));
    } catch (IOException e) {
      e.printStackTrace();
    }

    try (var in = new ObjectInputStream(new FileInputStream(file))) {
      var a = (Article)in.readObject();
      System.out.println(a);
    } catch (ClassNotFoundException | IOException e) {
      e.printStackTrace();
    }
  }
}
```