[Java] ファイル書き込みは何を使えばいいのか

# 背景
ファイル読み書きってたくさんクラスがあるが、何クラスを使用するのがベストなのか分かっていませんでした。
昔は、別プロジェクトでのコードを ~~コピペ~~ 参考にしていました。
なので、調べてみました。
結論から言うと、調べれば調べるほど混乱しました。
そして、これを使えばいい！！というものが見つかったわけでもありません。

# Streamってなに？
データをバイト単位で処理する。
~~ちなみに、Streamというクラスは存在しない。~~
インターフェース
https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html

# InputStream
バイトで**「read」**を行うクラス。

# OutputStream
バイトで**「write」**を行うクラス。

# FileInputStream
バイトを読み込むためのクラス。
InputStreamの子クラス。
だいぶ古い。

# FileOutputStream
バイトを書き込むためのクラス。
OutputStreamの子クラス。
だいぶ古い。

# InputStreamReader
InputStreamをReaderに渡す役割。
ここで文字コードを指定する。

# OutputStreamWriter
OutputStreamをWriterに渡す役割。
ここで文字コードを指定する。

# Reader
ストリームからバイトではなく、文字単位（コードで言うとchar[]）で**「read」**を行う。

# Writer
ストリームからバイトではなく、文字単位（コードで言うとchar[]）で**「write」**を行う。

# バッファリングってなに？
メモリにためこんで、一定量たまったら一気に読み書きする機能のこと。
これにより、1バイトずつ読み書きするパフォーマンスが良い。

# BufferedReader
Readerのバッファリング機能つき

# BufferedWriter
Writerのバッファリング機能つき


# ファイル書き込み機能を実装してみた

```java
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FileCreate {
    public static void main(String[] args) {

        // 読み書きするファイルパス
        Path path = Paths.get("C:\\demo.txt");
        // 書き込む文字列
        List<String> lines = Stream.of("あいうえお", "かきくけこ", "さしすせそ").collect(Collectors.toList());

        // ファイル書き込み１
        // 文字コードはデフォルトでUTF-8
        // OutputStreamWriterを使用している
        try (BufferedWriter writer = Files.newBufferedWriter(path)) {
            for (String str : lines) {
                // nullを渡すと、nullという文字列として書き込むようので注意
                writer.append(str);
                writer.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // ファイル書き込み２
        // 内部的にはBufferedWriterとOutputWriter、FileOutputStreamを使用している
        try {
            // 新規で書き込みたいとき
            Files.write(path, lines, Charset.forName("Shift_JIS"), StandardOpenOption.WRITE);
            // 追記したいとき
            Files.write(path, lines, Charset.forName("Shift_JIS"), StandardOpenOption.APPEND);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // ファイル書き込み３
        // printWriter
        try (
            // 追記したいときはtrue、新規のときはfalse
            FileOutputStream fileOutputStream = new FileOutputStream(path.toFile(), true);
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, Charset.forName("Shift_JIS"));
            PrintWriter printWriter = new PrintWriter(outputStreamWriter)) {

            for (String s : lines) {
                printWriter.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // ファイル書き込み４
        // BufferedWriter
        try (
            // 追記したいときはtrue、新規のときはfalse
            FileOutputStream fileOutputStream = new FileOutputStream(path.toFile(), true);
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, Charset.forName("Shift_JIS"));
            BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter)) {

            for (String s : lines) {
                bufferedWriter.append(s);
                bufferedWriter.newLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

簡潔に済ませるのならば、ファイル書き込み１か２で良いかと思います。
ただ、何かと自分好みに書き込み内容をカスタマイズしたいならば、ファイル書き込み３か４を使用しようと思います。

ファイル書き込み３と４は、自分に分かりやすく、ひとつひとつ変数宣言しています。
1行にまとめるのも、もちろんOKです。

```java
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path.toFile(), true), Charset.forName("Shift_JIS")));
```

注意すべき点は、close()を忘れないことです。

# 終わりに

2日くらいかけて、調べてみたのですが、大まかには理解できました。
ただ、詳細が理解できていないなと感じています。
もし、間違っている、もしくはこのコードは無駄では？という箇所があったらご指摘いただけたらと思います。