Java Programmer Gold SE 8 資格まとめ（Java に慣れてる人向け）

# 記事の目的

Java Programmer Gold SE 8 試験に出題される内容をまとめました。
「昔から Java をやっているけど SE8 のことは微妙に知らない」というニッチな層向けです。

# この記事を書いたきっかけ

先日 Java SE 8 Programmer II (1Z0-809) を受験して合格し、Oracle Certified Java Programmer, Gold SE 8 をゲットしました。
Java2 の時に一度取得しましたが、今の世間で必要とされている知識レベルとは明らかに乖離していたので、自分の知識をアップデートする意味でも勉強し直した次第です。Android 開発でも SE8 の知識は必須となりつつありますしね。
受験にあたって試験範囲を自分なりにまとめたものがあるのですが、試験終了とともにそのままお蔵入りさせるのも忍びないので、記事というかたちで残しておこうと思いました。どなたかのためになれば、大変嬉しく思います。

# 要点まとめ

## Java の基本

#### 数値リテラルの接頭辞

数値の先頭に付加される接頭辞によって何進数の数値かが決定する

* "0b"…2進数
* "0"…8進数
* なし…10進数
* "0x"…16進数

#### final変数を初期化するタイミング

final 修飾子を付与したインスタンス変数は、コンストラクタで初期化されていなくてはならない  

#### イニシャライザやコンストラクタによる初期化の順番

オブジェクトの初期化に関わる処理は以下の順番で実行される

1. static イニシャライザ  
クラスのロード時
2. インスタンスイニシャライザ  
インスタンス生成時、コンストラクタ実行前  
変数を宣言しただけでは実行されない。インスタンス化が必要
3. コンストラクタ

#### 列挙値の継承とインターフェイスの実装

* 列挙型のメソッドである `name()` と `toString()` は自身の文字列表現を返す
* `name()` は final なのでオーバーライド不可。`toString()` は可
* 列挙型はインターフェイスの実装と、抽象メソッドの定義ができる。これらをした場合、各列挙値でオーバーライドする必要がある
* 文字列から列挙値を得る場合、`valueOf()` メソッドが利用できる

#### 列挙値についての注意点

* 列挙値にはコンストラクタを定義できるが、new によるインスタンス化は許可されていないので、コンストラクタに public や protected をつけるとコンパイルエラーになる  
何もつけなければコンパイルは通るが、new することはできない（コンパイルエラーになる）
* final 宣言のないフィールドを定義すると、そのフィールドの値は任意のタイミングで変更することができる

```java
enum Direction {
    North(0), South(1);

    // final 宣言しなければ、利用する側でこの値を変更できる
    int index;
    private Direction(int index) { this.index = index; }
}

// Direction の index フィールドを変更してみた例
Direction d = Direction.North;
System.out.println(d.index);    // 0
d.index = 10;
System.out.println(d.index);    // 10
```

#### hashCode() と equals()

* 複数のオブジェクトを同値とする条件は下記2点
    * `hashCode()` の戻り値（ハッシュ値）が同じ
    * `equals()` が true
* コレクションでの重複チェックは以下の通り。両者が成立したら同値とみなされる
    1. ハッシュ値が同じかチェック
    2. ハッシュ値が同じものだけを対象に `equals()` で比較
* `hashCode()` を実装する場合、同値のオブジェクトからは値が返るよう実装する
* `equals()` の引数は Object。レシーバと同じ型にするとオーバーライドは成立せず、オーバーロードが成立してしまう。コンパイルエラーにはならない

#### インターフェイスのメソッド

* インターフェイスに定義するインスタンスメソッドは public な抽象メソッドであればよい。abstract などの記述があってもコンパイルは通る
* 複数のインターフェイスに同名の default メソッドが定義されている状況で、両方を継承または実装すると default メソッドが競合してコンパイルエラーになる  
ただし、継承または実装する側で default メソッドをオーバーライドするとエラーは解消する

```java
interface Sample {
    // "public abstract"が有っても無くてもコンパイルは通る
    public abstract void execute();
}

interface A { default void execute() { System.out.println("A"); } }
interface B { default void execute() { System.out.println("B"); } }
interface C extends A, B {
    // このコメントを外すとコンパイルエラーが解消する
    // default void execute() { System.out.println("C"); }
}
```

#### オーバーライドが成立するための条件

* メソッドをオーバーライドする際、戻り値はスーパークラスの型と同じかそのサブクラスでなければいけない
* メソッドのオーバーライドは、以下が一致しなければ成立しない。順番が一致しなくてもオーバーロードとなる
    1. メソッド名
    2. 引数の型
    3. 引数の順番
* static メソッドや static フィールドもサブクラスで再定義できる  
ただしオーバーライドではなく「隠蔽」なので、super キーワードを使って親の static メソッドを呼び出すことはできない

#### メソッドローカルのクラス

* メソッドローカルなクラスからメソッド内の変数を直接参照できるが、変数は実質的に final でないとダメ。final 宣言が無くても、メソッドローカルクラスから参照すると自動的に final 扱いになる。これはラムダも同様
* コンストラクタでメソッドの変数をメソッドローカルなクラスに与える場合は、元の変数を直接参照しないので final にしなくてもよい
* メソッドローカルなクラスからメソッドの外側のクラスのメンバ変数を参照する場合も、そのメンバ変数を変更して構わない

#### ラムダ式

1. 引数のデータ型は省略可
2. 引数が1つなら『()』が省略可
3. 右辺の式が1つなら、右辺からの戻り値があっても無くても『{}』が省略可
4. 右辺の式が1つで、かつ戻り値を返すなら return が省略可

## 関数型インターフェイス

#### Function 関連

|インターフェイス|メソッド|戻り値の型|
|:---|:---|:--:|
|`Function<T, R>`|`apply(T t)`|R|
|`UnaryOperator<T>`|`apply(T t)`|T|
|`BinaryOperator<T>`|`apply(T t1, T t2)`|T|
|`BiFunction<T, U, R>`|`apply(T t, U u)`|R|

#### Consumer 関連

|インターフェイス|メソッド|戻り値の型|
|:---|:---|:--:|
|`Consumer<T>`|`accept(T t)`|void|
|`BiConsumer<T, U>`|`accept(T t, U u)`|void|

#### Predicate 関連

|インターフェイス|メソッド|戻り値の型|
|:---|:---|:--:|
|`Predicate<T>`|`test(T t)`|boolean|
|`BiPredicate<T, U>`|`test(T t, U u)`|boolean|

#### Supplier

|インターフェイス|メソッド|戻り値の型|
|:---|:---|:--:|
|`Supplier<T>`|`get()`|T|

#### プリミティブ型を受け取る関数型インターフェイス

* int、long、double の3つにはそれぞれ専用の関数型インターフェイスが存在する  
**boolean にも BooleanSupplier だけ** は存在する
* `Supplier`、`UnaryOperator`、`BinaryOperator` のメソッド名は下記に従う  
**ルール：【メソッド名】As【データ型】**  
例：`applyAsInt`、`getAsBoolean` など

|データ型|Function|Consumer|Predicate|Supplier|UnaryOperator|BinaryOperator|
|:---|:--:|:--:|:--:|:--:|:--:|:--:|
|int|`IntFunction<R>`|`IntConsumer`|`IntPredicate`|`IntSupplier`|`IntUnaryOperator`|`IntBinaryOperator`|
|long|`LongFunction<R>`|`LongConsumer`|`LongPredicate`|`LongSupplier`|`LongUnaryOperator`|`LongBinaryOperator`|
|double|`DoubleFunction<R>`|`DoubleConsumer`|`DoublePredicate`|`DoubleSupplier`|`DoubleUnaryOperator`|`DoubleBinaryOperator`|
|boolean|-|-|-|`BooleanSupplier`|-|-|

#### プリミティブ型を返す関数型インターフェイス

|インターフェイス名|メソッド|説明|
|:---|:---|:---|
|`ToIntFunction<T>`|`applyAsInt(T t)`|T型の値から int を返す|
|`ToIntBiFunction<T, U>`|`applyAsInt(T t, U u)`|T型とU型の値から int を返す|
|`ToLongFunction<T>`|`applyAsLong(T t)`|T型の値から long を返す|
|`ToLongBiFunction<T, U>`|`applyAsLong(T t, U u)`|T型とU型の値から long を返す|
|`ToDoubleFunction<T>`|`applyAsDouble(T t)`|T型の値から double を返す|
|`ToDoubleBiFunction<T, U>`|`applyAsDouble(T t, U u)`|T型とU型の値から double を返す|

#### メソッド参照

* 関数型インターフェイスの中だけで使える
* 引数を取る場合は static メソッド参照と同じ書き方にする

## コレクション

#### Arrays#asList

* `Arrays#asList` で生成されるリストは、引数に与えられた配列を参照しているだけ。元の配列に加えた変更もリストに反映される
* `Arrays#asList` で生成されるリストは `ArrayList<E>` と **互換性がない。** キャストしても失敗する

#### コレクションクラスの性質と見分け方

* `List` は原則インデックスの順番
* `Set` と `Map` はクラス名のプレフィクスによって性質を見分ける
    * `Linked` で始まるクラスは、挿入した順序が保たれる
    * `Tree` で始まるクラスは自然順などによってソートされる。自然順で並べると **数字→英字(大)→英字(小)** となる

#### Queue インターフェイスの性質

* 挿入／削除／検査のメソッドが2種類あり、メソッドの実行に失敗した時の挙動が違う
    * 例外が発生
        * 挿入 … `add(E)`
        * 削除 … `remove()`
        * 検査 … `get()`
    * null を返却
        * 挿入 … `offer(E)`
        * 削除 … `poll()`
        * 検査 … `peek()`
* インデックスで要素にアクセスできない

#### Deque インターフェイスの性質

* `Queue` との違いは、 **Double Ended Queue** の名前通り、先頭または末尾に要素を挿入または削除できること
    * 例外が発生
        * 挿入 … `addFirst(E)`, `push(E)` / `addLast(E)`
        * 削除 … `removeFirst()` / `removeLast()`
        * 検査 … `getFirst()`, `pop()` / `getLast()`
    * null を返却
        * 挿入 … `offerFirst(E)` / `offsetLast(E)`
        * 削除 … `pollFirst()` / `pollLast()`
        * 検査 … `peekFirst()` / `peekLast()`
* `Queue` と同じで、インデックスでは要素にアクセスできない
* `java.util.concurrent` パッケージにある `Queue` インターフェイスの拡張にはタイムアウトを設定できるメソッドもある  
常に待ち時間が発生する訳ではなく、挿入や削除ができないとき待機が発生する
* `Deque` の実装である `ArrayDeque` には null を入れることができない

## ジェネリクス

#### ジェネリクスと型パラメータ

* クラスに対して型パラメータを宣言する場合、以下の用途で型パラメータは使用できない
    * static メンバ
    * new 演算子によるインスタンスや配列の生成
    * `instanceof` による型の検証
    * `.class` の参照
* 型パラメータが適用されたメソッドを使うときは型を明示してもしなくてもよい
* 総称型の構文は以下の通り  
**class クラス名<型パラメータ(, 型パラメータn)> {...}**
* クラス宣言で型パラメータを使う場合、extends キーワードは使えるが super キーワードは使えない
* メソッドの構文は以下の通り  
宣言： **<型パラメータ> 戻り値型 メソッド名(型 引数名) {...}**  
使用： **レシーバ.<型パラメータ>メソッド名(型 引数名) {...}**  

#### ワイルドカード

* ワイルドカードは `"*"` ではなく `"?"` （ `"*"` はKotlin）
* ワイルドカードを class 宣言の型パラメータに使うことはできない
* ワイルドカードの型は実行されるまで確定しないので、ワイルドカードを使って中のデータ型を指定したリストに追加するとコンパイルエラーになる  
例外として `<? super XXXXX>` で宣言されたリストに XXXXX と同じ型のオブジェクトを入れることはできる
* ワイルドカードを使った場合、宣言されている内容から判断できる範囲で問題がなければコンパイルエラーにはならない。型に互換性がないことが明確になったら、コンパイルエラーになる

#### 型パラメータにワイルドカードを使った場合にコンパイルエラーが起きるところ

* 型パラメータにワイルドカードを使用すると、 **実際にオブジェクトを追加するところ** でコンパイルエラーになる

## Comparable と Comparator

#### Comparable<T> インターフェイス

* `java.lang` パッケージに所属
* 比較される対象になるためのインターフェイス
* `Comparable` で実装すべきメソッドは `compareTo(T o)` のみ。比較相手より小さい場合は負の値を返し、等しい場合はゼロを、大きい場合は正の値を返す
* クラス宣言時に `Comparable<T>` の型パラメータに与えるのは `compareTo` が引数に取るオブジェクトの型

#### Comparator<T> インターフェイス

* `java.util` パッケージに所属
* 比較するためのインターフェイス。なお比較対象は `Comparable` インターフェイスを実装しているものに限らない
* `Comparator` で実装すべきメソッドは以下の2つ（`equals` は任意）
    * `compare(T t1, T t2)`
    * `equals(T o)`
* `compare(T t1, T t2)` では、t1がt2より大きい場合は正の値、t1とt2が等しい場合はゼロ、t1がt2より小さい場合は負の値を返す

## Stream API

#### 要素数が有限のストリームを生成

* `Collection#stream()` -> `Stream<E>`
* `Arrays#stream(E[] array)` -> `Stream<E>`
* `Arrays#stream(int[] array)` -> `IntStream`
* `Arrays#stream(long[] array)` -> `LongStream`
* `Arrays#stream(double[] array)` -> `DoubleStream`
* `Stream#of(E… values)` -> `Stream<E>`
* `Stream#empty()` -> `Stream<E>`
* `IntStream#of(int… values)` -> `IntStream`
* `LongStream#of(long… values)` -> `LongStream`
* `DoubleStream#of(double… values)` -> `DoubleStream`
* `IntStream#range()` ／ `IntStream#rangeClosed()` -> `IntStream`
* `LongStream#range()` ／ `LongStream#rangeClosed()` -> `LongStream`
* `BufferedReader#lines()` -> `Stream<String>`
* `java.nio.file.Files#lines()` -> `Stream<String>`

#### 要素数が無限のストリームを生成

どちらも `java.util.stream.Stream` のメソッド

* `generate(Supplier<T> supplier)`  
supplier から提供される要素を無限に保持する `Stream` を生成する
* `iterate(T seed, UnaryOperator<T> f)`  
seed が最初の要素で、それ以降は前の要素に f をかけ合わせた要素を無限に保持する Stream を生成する

#### ストリームの終端操作

* 同じストリームへの終端操作は1度だけ。複数回行うと `IllegalStateException` が発生する
* 中間操作を呼び出しただけでは何も実行されず、終端操作を呼び出さない限り何も行われない。終端操作を呼び出すと全ての処理が実行される
* パイプライン処理では、ストリーム内の要素が1つずつ順番に全ての中間／終端操作を実行されてから次の要素が処理される  
つまり **「要素1の中間操作×m→要素1の終端操作」⇒「要素2の中間操作×m→要素2の終端操作」⇒…⇒「要素nの中間操作×m→要素nの終端操作」** という順番

#### Stream#reduce の戻り値

`Stream#reduce` は `Stream` の中身を集約するためのメソッド。第1引数に初期値を設定するほうのメソッドでは、戻り値が `Optional` じゃない

* `reduce(BinaryOperator op)` … 戻り値は `Optional<T>`
* `reduce(T initial, BinaryOperator op)` … 戻り値は T

#### 戻り値が Optional の終端操作

* 戻り値が `Optional` になるのは以下のメソッド
    * `findAny()` … 実行するたびに結果が変わる可能性がある
    * `findFirst()` … 何度実行しても同じ結果が得られる
    * `min()`
    * `max()`
    * `reduce(BinaryOperator op)`
* `Stream` の型によって `Optional` の型が違う。例えば `findAny()` なら以下の通り
    * `Stream#findAny()` … `Optional<T>`
    * `IntStream#findAny()` … `OptionalInt`
    * `LongStream#findAny()` … `OptionalLong`
    * `DoubleStream#findAny()` … `OptionalDouble`
    
#### Optional のインスタンスを生成する

* `Optional#of(T value)` … valueにnullは入れられない
* `Optional#ofNullable(T value)` … valueにnullを入れられる
* `OptionalInt#of(int value)`
* `OptionalLong#of(long value)`
* `OptionalDouble#of(double value)`

#### Optional#ifPresent と Optional#isPresent

* 値があるかどうか(null or not null)を判定するためのメソッドは以下の2つ
    1. `isPresent()` … 値の有無を判定する
    2. `ifPresent(Consumer)` … 値があれば `Consumer` を実行する
    
#### Optional から値を取り出すためのメソッドは複数存在する

`Optional<T>` から値を取得するメソッドは以下。値が存在する場合はその値が得られるが、存在しない場合の挙動が異なる

* `get()`  
存在しない場合は `NoSuchElementException`
* `orElse(T)`  
存在しない場合は引数を返す
* `orElseGet(Supplier<T>)`  
存在しない場合は `Supplier` から返される値を返す
* `orElseThrow(Supplier<X extends Exception>)`  
存在しない場合は `Supplier` から返される例外をスローする

#### 特定のデータ型に特化した Optional の種類

`Optional<T>` の関連クラスと値を取り出すためのメソッドは以下

* `Optional<T>#get()`
* `OptionalInt#getAsInt()`
* `OptionalLong#getAsLong()`
* `OptionalDouble#getAsDouble()`

#### Stream と XXXStream の型変換

* 同じ `Stream` 型同士の変換は `map()`
* `Stream<T>` 型への変換は `mapToObj()`
* `IntStream` 型への変換は `mapToInt()`
* `LongStream` 型への変換は `mapToLong()`
* `DoubleStream` 型への変換は `mapToDouble()`

#### Stream#collect() メソッドと Collector クラス

* `collect()` は `Stream` 内の要素を集約して、1つのオブジェクトを取得するメソッド
* `Collectors` クラスは用途に合う `Collector` インターフェイスの実装を提供する  
その性質上、`Stream` クラスの一部メソッドと役割がかぶる

## 例外

#### Error、Exception、 RuntimeException

* 全て `Throwable` のサブクラスで、`java.lang` パッケージに所属
* `Error` と `Exception` は `Throwable` のサブクラス
* `RuntimeException` は `Exception` のサブクラス（`Throwable` の孫クラス）
* `try〜catch` が必須となる checked 例外は `RuntimeException` 以外の `Exception` のサブクラスだけ

#### 例外を catch する順番

* 例外はサブクラスから順番に catch すること
* スーパークラスをサブクラスより先に catch するとコンパイルエラーになる  
※実行時エラーではない

#### 例外のマルチキャッチ

* 継承関係のある複数の例外をまとめてキャッチするとコンパイルエラー
* マルチキャッチしたときの例外オブジェクトは暗黙的に final 扱いになり再代入は不可  
マルチキャッチでない場合は、final 扱いにならない

#### 例外の rethrow

* 1つのメソッドが複数種類の例外オブジェクトを catch して rethrow する場合、`throws` および `catch` メソッドで取る例外は一致しなくてもよい
* catch で起こりうる全ての例外の親クラスを拾って、そのまま throw する場合など

```java
// SE7以降では、2つの例外をまとめて catch してもよい
// executeからthrowされるのも、Exceptionではなく IllegalAccessExceptionとArighmeticException
private void execute(int number) throws IllegalAccessException, ArithmeticException {
    try {
        switch (number) {
            case 1:
                throw new IllegalAccessException();
            case 2:
                throw new ArithmeticException();
            default:
                System.out.println("No exception");
        }
    } catch (Exception e) {
        throw e;
    }
}
```

#### 例外をthrowするメソッドのオーバーライド

* `throws` 宣言つきのメソッドをサブクラスでオーバーライドする場合、オーバーライドしたメソッドに `throws` 宣言をするかどうかは自由
* オーバーライドしたメソッドでも `throws` を宣言する場合の例外は、元のメソッドと同じかそのサブクラスでなければならない
* `RuntimeException` とそのサブクラスは無条件で `throws` に指定できる。ただし非チェック例外なので、呼び出し元で catch しなくてもコンパイルは通る

#### try〜with〜resources 文

* `try〜with〜resources` 文でのみ `try` 単体での使用が可能
* 使用するリソースは `java.lang.AutoCloseable` か、そのサブインターフェイスである `java.io.Closeable` を実装する必要がある
* `AutoCloseable`／`Closeable` はどちらも `close()` に `throws` 宣言があるため、リソースに使う場合も `try` の中で使う場合も、例外を catch しないとコンパイルエラー
    * `AutoCloseable#close() throws Exception`
    * `Closeable#close() throws IOException`
* `AutoCloseable` もしくは `Closeable` の `close()` で例外が発生したら、catch で補足される
* `try` ブロックとリソースのクローズの両方で例外が発生したら、`close()` で起きた例外は抑制された例外として catch された例外オブジェクトに `Throwable` の配列で格納され、`getSuppressed()` で取得が可能
* `try〜with〜resources` 文で例外が起きたときの実行順序は以下
    1. `try` ブロック
    2. 宣言した逆順にリソースをクローズ（下記のコードだと、Nから1へ順番に閉じる）
    3. catch ブロック
    4. `finally` ブロック

```java
// (1) -> ResourceNから1の順番にclose() -> (2) -> (3) の順に実行
try (Resource1; Resource2; ...; ResourceN) {
    (1)
} catch (Exception e) {
    (2)

    // getSuppressed() で撮れるのは Throwable の配列
    for (Throwable th : e.getSuppressed()) { ... }
} finally {
    (3)
}
```

#### アサーションとその利用方法

* アサーションは、`assert` 文で false が検出されたら `AssertionError` を投げる機能で、文法は以下の通り
`assert boolean式: エラーメッセージ;`
* boolean 式は括弧で括っても括らなくてもOK
* エラーメッセージは省略可
* アサーションはデフォルトでは無効。java コマンドで "-ea" オプション（"enable assertion" の略？）を明示的に指定しないと、AssertError はスローされない
* 明示的に無効にするには、java コマンドで "-da" オプション（"disable assertion" の略？）を指定する
* アサーションの対象だと明示したいなら `java -ea:【クラス名】` として、コロン区切りでアサーションの対象（-daなら非対象）を指定する

## Date and Time API

#### クラス一覧

1. `LocalDate` … 日付
2. `LocalTime` … 時刻
3. `LocalDateTime` … 日付＋時刻
4. `OffsetTime` … UTC/GMTからの差分を「+hh:mm」で含む時刻
5. `OffsetDateTime` … UTC/GMTからの差分を「+hh:mm」で含む日付＆時刻
6. `ZonedDateTime` … UTC/GMTからの差分をタイムゾーンIDで含む日付＋時刻

* 全て `Temporal` インターフェイスを実装する。更にそのスーパーインターフェイスは `TemporalAccessor`
* "Local〜" と "Offset〜" と "Zoned〜" の違いは、同じ日時を表現する方式の違い
* `LocalDateTime#toLocalDate()` と `LocalDateTime#toLocalTime()` でそれぞれへの変換が可能
* 月を取得できるメソッドは2つ
    * `getMonth()` … `Month` 列挙値で取得
    * `getMonthValue()` … int 値で取得
    
#### 列挙型一覧

1. `Month`：月を表す  
`JANUARY`, `FEBRUARY`, `MARCH`, …, `DECEMBER`
2. `DayOfWeek`：曜日を表す  
`SUNDAY`, `MONDAY`, `TUESDAY`, …, `SATURDAY`

#### LocalDate / LocalTime / LocalDateTime のインスタンス化

1. `now()`
2. `of(year, month, dayOfMonth, ...)`  
それぞれのクラスが扱える値を引数にしてインスタンスを生成する  
引数 month には整数でなく `Month` 列挙値をセットしてもOK  
`LocalDateTime` のみ `LocalDate` と `LocalTime` を引数に取るオーバーロードがある。年、月、日、時、分、秒、ミリ秒を引数に取るのもある
3. `parse(String)`、`parse(String, DateTimeFormatter)`  
文字列からインスタンスを生成  
`DateTimeFormatter` を併用すると任意のフォーマットの文字列でも可

#### LocalDate#of および LocalTime#of のオーバーロード

* `LocalDate#of` には2つのオーバーロードメソッドがある。どちらも年月日は指定するが、それ以外の違いは以下の通り
    1. 第2引数が int
    2. 第2引数が `Month` 列挙値
* `LocalTime#of` には3つののオーバーロードメソッドがあり、全て時分は指定するが、それ以外の違いは以下の通り
    1. 時分のみ
    2. 時分に加えて秒を指定
    3. 時分に加えて秒とナノ秒を指定
    
#### java.time.format.DateTimeFormatter

1. `ofPattern(String)`  
フォーマットパターンを指定
2. `ofLocalizedDate(FormatStyle)`  
日付のフォーマット方式を指定
3. `ofLocalizedTime(FormatStyle)`  
時間のフォーマット方式を指定
4. `ofLocalizedDateTime(FormatStyle)`  
日時のフォーマット方式を指定
5. `ofLocalizedDateTime(FormatStyle, FormatStyle)`  
日時のフォーマット方式を、日付と時間で別々に指定
6. static で事前定義されたフォーマッタ  
`BASIC_ISO_DATE` など

#### java.time.format.FormatStyle

`FormatStyle` は4種類

* `FormatStyle.FULL`  
タイムゾーンを含む完全な書式。ゾーン情報を持たない LocalDateTime などに適用すると実行時に例外
* `FormatStyle.LONG`  
タイムゾーンを含む簡潔な書式。ゾーン情報を持たない LocalDateTime などに適用すると実行時に例外
* `FormatStyle.MEDIUM`  
タイムゾーンを含まない簡潔な書式
* `FormatStyle.SHORT`  
タイムゾーンを含まない簡潔な日時までの書式

```java
LocalDateTime dateTime = LocalDateTime.of(2020, 8, 14, 9, 54, 30);
ZonedDateTime zDateTime =
    ZonedDateTime.of(dateTime, ZoneId.systemDefault());

// FormatStyle.FULL … 『2020年8月14日 9時54分30秒』
println(fmt1.format(zDateTime));

// FormatStyle.LONG … 『2020/08/14 9:54:30 JST』
println(fmt2.format(zDateTime));

// FormatStyle.MEDIUM … 『2020/08/14 9:54:30』
println(fmt3.format(dateTime));

// FormatStyle.SHORT … 『20/08/14 9:54』
println(fmt4.format(dateTime));
```

#### Date and Time API のフォーマット文字列

* "y" … 年
* "M" … 月（大文字）
* "d" … 日
* "H" … 時（大文字）
* "m" … 分
* "s" … 秒
* "S" … ミリ秒（大文字）
* "n" … ナノ秒

#### 日付／時刻の加減算

加算 … `plus(TemporalAmount amountToAdd)`
減算 … `minus(TemporalAmount amountToSubtract)`

* `Period` や `Duration` が `TemporalAmount` インターフェイスを実装する
* `plusYears` や `plusWeeks` など個々のフィールドに対する加減算も可能だが、複数のフィールドをまとめて扱う場合は `Period` や `Duration` を引数にして plus を呼び出す

#### 夏時間

* 夏時間の適用開始日（時間を進める日）と適用終了日（時間を戻す日）は、同じ地域でも毎年異なる。ただし時刻は 02:00 で一定
* 適用が開始されると、オフセットが1時間減り時刻が1時間進む  
02:00 から1時間進むので、01:59 の1分後は 03:00 になる  
適用開始日に 01:00 と 03:00 の間隔を取ると、2時間ではなく1時間
* 適用が終了すると、オフセットが1時間増え時刻が1時間戻る  
02:00 から1時間戻るので、01:59 の1分後は 01:00 になる

#### Period：年月日

* `toString()` した Period 表現は **"P00Y00M00D"** となる  
先頭の "P" は "Period" の頭文字
* `Period` のインスタンスを生成する方法はいくつかある  
    * `between` メソッドで2つの `LocalDate` や `LocalDateTime` の差分を取る
    * `ofYears(1)` など ofXXXXX メソッドで生成。ただしメソッドチェーンは不可
* `Period` は年、月、日、週で加減算できる  
加減算のメソッドは plusXXXXX / minusXXXXX。`plusYears(1)` など  
`plusWeeks(3)` で週を加算すると "P21D"。週の `Period` 表現が無い為
* `LocalDate` / `LocalDateTime` は `plus` / `minus` メソッドで `Period` を引数にして日付の加減算ができる

```java
// Period を使った年の加算
LocalDateTime dt = LocalDateTime.now();
Period p = Period.ofYears(1);

// これは以下の2行と同等（メソッドチェーンは意味がない）
// Period p1 = Period.ofDays(1);
// Period p = Period.ofYears(2);
Period p = Period.ofDays(1).ofYears(2);
```

#### Duration：時分秒

* `toString()` した `Duration` 表現は **"PT00H00M00S"** となる  
先頭の "PT" は "Period of Time" の略称
* `Duration` のインスタンスを生成する方法はいくつかある  
    * `between` メソッドで2つの `LocalDateTime` や `LocalTime` の差分を取る
    * `ofHours(1)` など ofXXXXX メソッドで生成
    * `of(1, ChronoUnit.HOURS)` など of メソッドで `ChromeUnit` を併用して生成
* `Duration` は日、時、分、秒、ミリ秒、ナノ秒で加減算できる  
日を加算したら PT24H で、ミリ秒以下だと PT0.13S となる
* `LocalTime` / `LocalDateTime` は `plus` / `minus` メソッドで `Duration` を引数にして時分秒の加減算ができる

#### java.time.Instant

* エポック時間からの経過を表現するクラス
* インスタンスを生成する方法はいくつかある
    1. `Instant#now()`
    2. `Instant#ofEposSecond(long)`
    3. `Instant#ofEpocMilli(long)`
    4. `LocalDateTime#toInstant(ZoneOffset)`
    5. `ZonedDateTime#toInstant()`
* `LocalDateTime#toInstant` で `ZoneOffset` が必要なのは `LocalDateTime` にタイムゾーンの情報が含まれていない為
* `Instant` のタイムゾーンは常にUTC

## 入出力

#### java.io.Reader を使った読み込み

* `Reader`
    * `public int read()`  
読み込んだ1文字を返す。末尾に到達したら **-1**
    * `public int read(char[] buffer)`
    * `public int read(char[] buffer, int offset, int length)`  
読み込んだ文字を配列の一部または全部に入れて文字数を返す。末尾に到達したら **-1**
* `BufferedReader`
    * `public String readLine()`  
読み込んだ1行のテキストを返す。末尾に到達したら **null**

#### java.io.Writer を使った書き込み

* `Writer`
    * `public void write(char[] buffer)`
    * `public void write(char[] buffer, int offset, int length)`  
配列の一部または全部を書き込む
    * `public void write(int c)`  
1文字書き込む
    * `public void write(String str)`
    * `public void write(String str, int offset, int length)`  
文字列の一部または全部を書き込む
* `BufferedWriter`
    * `public void newLine()`  
改行文字を書き込む

#### java.io.Reader や java.io.InputStream の読み込み位置制御

* `skip(long bytes)`  
現在位置から指定したバイト数だけ読み飛ばす
* `mark(int readAheadLimit)`  
現在位置をマークして `reset()` で戻れるようにする。引数は **マークを維持しつつ読み込むことができる文字数の上限**
* `reset()`  
`mark()` した位置に戻る

```java
// ファイルの中身は "01234567"
// 実行結果は "025676"
try (BufferedReader br =
        new BufferedReader(new FileReader("sample.txt"))) {
    for (int i = 0; i < 3; i++) {
        br.skip(i); // iバイトを読み飛ばす
        System.out.print((char)br.read());
    }
    br.mark(3); // 現在位置("6"のあるINDEX=5の場所)をマーク
    System.out.print(br.readLine());
    br.reset(); // マークした位置(INDEX=5の場所)に戻る
    System.out.println((char)br.read());
} catch (IOException e) {
    e.printStackTrace();
}
```

#### java.io.PrintWriter

* `java.io.Writer` のサブクラスで、プリミティブ型をそのまま出力できる  
boolean/char/char[]/int/float/long/String/Object が対象
* `print` と `println` 以外に `format` を使って文字列のフォーマットが可能
* 自動フラッシュ機能を持つ
* 同じ機能を持つクラスに `PrintStream` がある  
JDK1.2で `PrintWriter` にとって変わられたが、後方互換のため残っている

#### java.io.Console

* 標準入力からデータを読み取る
* `System#console()` でシングルトンインスタンスを取得。 **利用不可なら null が返る**
* `readLine()` で、入力された文字列を String で取得
* `readPassword()` で、入力された文字列を char[] で取得。パスワードの読み取りなので、入力時に文字列は表示しない
* `writer()` と `reader()` で、それぞれコンソールに紐づいている `PrintWriter` と `Reader` を取得できる
* `Console` 自身にも `printf` と `format` メソッドがあり、コンソールに文字列を出力できる

#### シリアライズに関する注意点

* `java.io.Serializable` インターフェイスを実装したクラスはシリアライズ可能
* シリアライズ対象外になるのは以下のどちらか
    1. static 変数
    2. transient 宣言されているメンバ変数
* スーパークラスがシリアライズ可能なら、サブクラスもシリアライズ可能
* デシリアライズでは、インスタンス初期化ブロックも、コンストラクタも呼び出されない。ただし static 初期化ブロックは呼び出される  
また、以下のケースならスーパークラスのコンストラクタだけは呼び出される
    1. 自身は `Serializable` だが、スーパークラスが `Serializable` ではない
    2. スーパークラスに引数なしのコンストラクタがある
    3. サブクラスのコンストラクタで明示的に親のコンストラクタを呼び出していない
* メンバ変数のシリアライズ可否は、フィールドではなく中身のデータ型で決まる  
変数の型が何であれ、中身が `Serializable` ならシリアライズ可能

## NIO2

#### java.nio.file.Path（特徴）

* 以下のメソッドで生成
    * `Paths#get(String, String...)`
    * `FileSystem#getPath(String, String...)`
* 主な特徴は以下の通り。`java.io.File` にない機能を備える
    * ファイルやディレクトリのパスを扱う
    * ファイル属性（オーナー、パーミッションなど）の取得や変更が可能
    * シンボリックリンクを扱える
    * ファイル作成など、ディレクトリで発生したイベントの変更や監視が可能
* `java.io.File` との相互変換が可能
    * `File#toPath()`
    * `Path#toFile()`

#### java.nio.file.Path（メソッド）

* `getRoot()`  
パスのルートを返す。相対パスで生成した Path からは null
* `subpath(int start, int end)`  
ルートを除く、インデックスで指定された範囲を抜き出した Path を生成する
* `getName(int)`  
ルートを除く、インデックスで指定された部分を String で返す
* `relativize(Path)`  
引数までの相対パスを返す
* `resolve(String)`、`resolve(Path)`  
パスを解決する。引数によって戻り値となる Path は異なる
    * 引数が相対パス … レシーバーに引数を連結して返す
    * 引数が絶対パス … 引数をそのまま返す
    * 引数が空 … レシーバー自身を返す
* `resolveSibling(String)`、`resolveSibling(Path)`  
レシーバーの親に対してパスを解決する。引数によって戻り値となる Path は異なる
    * 引数が絶対パス…レシーバーに引数を連結して返す
    * その他…レシーバーに引数を連結して返す
* `normalize()`  
「.」や「..」など冗長な部分を除去して適切な形に変換したパスを返す

```java
String sp1 = "/tmp/work1/sample.txt";
String sp2 = "/tmp/work2/dummy.txt";
Path p1 = Paths.get(sp1);
Path p2 = FileSystems.getDefault().getPath(sp2);
println(p1.getRoot());      // "/"
println(p1.subpath(0, 2));  // "tmp/work1"
println(p1.relativize(p2)); // "../../work2/dummy.txt"
println(p1.getName(0));     // "tmp"

Path rp = p1.resolve("../dat");
println(rp);                // "/tmp/work1/sample.txt/../dat"
println(rp.normalize());    // "/tmp/work1/dat"

// Windows の場合
Path wp = Paths.get("C:¥¥temp¥¥work¥¥sample.txt");
println(wp.getRoot());      // "C:¥"
```

#### java.nio.file.FileSystem

ファイルシステムへのインターフェイスを提供する。`FileSystems` クラスの static メソッドで取得

* `FileSystems#getDefault`
* `FileSystems#getFileSystem`
* `FileSystems#newFileSystem`

#### java.nio.file.Files（ファイル操作）

以下は全て static メソッド

* `copy`  
ファイルやディレクトリのコピー
    1. ディレクトリの中身まではコピーされない
    2. コピー先に同名のファイルが存在する時 CopyOption で上書き指定が無ければ例外
    3. デフォルトの挙動ではファイルをコピーしても属性はコピーされない
    4. CopyOption を指定せずにシンボリックリンクをコピーすると、リンクが示す実体がコピーされる
    5. 引数に与えるパスはコピー／移動をする場所ではなく、移動／コピー後のパス
* `move`  
ファイルやディレクトリの移動。挙動は copy の1〜5と同じ
* `getAttribute(Path, String, LinkOption...)`  
ファイルの属性値（ファイルサイズや更新日時など）を読み取る
* `Stream<String> lines(Path)`
* `List<String> readAllLines(Path)`  
ファイルの全ての行を返す。メソッドによって戻り値の型が異なる

#### java.nio.file.Files（ディレクトリの操作)

以下は全て static メソッド

* `createDirectory()`  
ディレクトリを作成するが、親は作成しない
* `createDirectories()`  
親も含めてディレクトリを作成する
* `delete` ／ `deleteIfExists`  
ファイルを削除する。失敗したら前者は例外、後者は boolean 型で結果が返る
どちらもディレクトリの中にファイルがあると削除できず、例外が発生する
* `newDirectoryStream(Path)`  
ディレクトリ内のパスを `Iterable` の実装クラス `DirectoryStream<Path>` で返す
* `list(Path)`  
ディレクトリ内のファイルやディレクトリの一覧を `Stream<Path>` で返す。引数がディレクトリでない場合は例外が発生する

#### java.nio.file.Files（ディレクトリの検索）

以下は全て static メソッド

* `walk()`
* `walkFileTree()`  
サブディレクトリも含めて再帰的に検索する
* `list()`  
そのディレクトリだけを検索する
* `find()`  
サブディレクトリも含めて再帰的に、条件に合致するものを検索する
* これらの戻り値は全てストリームなので、パイプライン処理が可能

#### java.nio.file.attribute

ファイルの属性（作成時間、アクセス時間、所有者など）を表現するクラスが入ったパッケージ。属性情報のセットを示す属性ビューインターフェイスも含む

* `BasicFileAttributes` インターフェイス  
基本的な属性情報
* `DosFileAttributes` インターフェイス  
DOS系の属性情報
* `PosixFileAttributes` インターフェイス  
Unix/Linuxの属性情報
* `AttributeView` インターフェイス  
属性情報のセット。サブインターフェイスとして以下が存在する
    * `BasicFileAttributeView`
    * `DosFileAttributeView`
    * `PosixFileAttributeView`

## 並列処理

#### 並行処理ユーティリティが提供する機能

1. スレッドプール  
スレッドの生成と再利用
2. 並行コレクション  
複数スレッドからの並行アクセスを適切に処理できるコレクション
3. アトミック変数  
並行アクセスによって値の不整合を起こさないための分割不可能な一連の操作を実装（synchronized と同じ）
4. カウウンティング・セマフォ  
有限なリソースに対して並行アクセス可能なスレッド数を自由に設定可能

#### 並列コレクション

* 並列処理に関わるAPIは以下。全て `java.util.concurrent` パッケージ
    * `BlockingQueue` インターフェイス
    * `BlockingDeque` インターフェイス
    * `ConcurrentMap` インターフェイス
    * `ConcurrentHashMap` クラス
    * `CopyOnWriteArrayList` クラス
    * `CopyOnWriteArraySet` クラス
* 並列処理に対応していないコレクションオブジェクトを複数のスレッドから操作すると `java.util.ConcurrentModificationException` が発生する
* 複数のスレッドから操作するときに拡張for文などでイテレータを使うと、イテレータを生成した時点のコレクションの中身が使用される

#### java.util.concurrent.ConcurrentMap インターフェイス

* `V getOrDefault(Object key, V defaultValue)`  
key に紐づく値を返す。無ければ defaultValue を返す
* `V putIfAbsent(K key, V value)`  
key に紐づく値がマップにない場合のみ追加
* `boolean remove(Object key, Object value)`  
key と value の両方が一致する要素があれば削除
* `V replace(K key, V value)`  
key に指定した値がマップにあれば置換
* `V replace(K key, V oldValue, V newValue)`  
key と oldValue の両方が一致する要素があれば値を newValue に置換

```java
// map の中身は [1, "One"], [2, "Two.2"], [3, "Three"]
Map<Integer, String> map = new ConcurrentHashMap<>();
map.put(1, "One");
map.putIfAbsent(1, "One.2"); // 追加しない(KEY=1の要素が既存)
map.put(2, "Two");
map.replace(2, "Two.2");           // 置換
map.replace(2, "Two.22", "Two.3"); // 置換しない(第2引数が既存の値と違う)
map.put(3, "Three");     // 削除
map.remove(3, "Three2"); // 削除しない(第2引数が既存の値と違う)
```

#### java.util.concurrent.CopyOnWriteArrayList クラス

* 内部で保持している要素を変更するときは、古い要素が入った配列をコピーしてそれを変更し、差し替える  
そのため、リストのサイズが大きくなるとオーバーヘッドが肥大化する
* 要素の変更中（新しい配列へのコピー中）に別のスレッドがリストにアクセスすると、古い配列の内容を参照することになる

#### java.util.concurrent.CyclicBarrier クラス

* スレッドパーティー（強調して動作するスレッドの集合）内の各スレッドの足並みを揃えるための機能を提供
* コンストラクタで協調させるスレッドの数を指定。第1引数は協調させるスレッドの数、第2引数があれば、トリップ（＝バリアが解除）したときに実行される処理
    * `new CyclicBarrier(int)`
    * `new CyclicBarrier(int, Runnable)`
* `await()` を実行したスレッドは `CyclicBarrier` のコンストラクタで指定した数のスレッドが await するまで待機する
    * `await(long timeout)`
    タイムアウトなし
    * `await(long timeout, TimeUnit unit)`
    タイムアウトあり
* 4つのスレッドに対して `new CyclicBarrier(3)` とすると、3つのスレッドが `await` した時点でトリップが発生する  
しかし最後の1つは、さらに2つのスレッドが `await` するまでバリアを突破できないので、続きの処理は実行できない

```java
// バリアの許容数が2つに対してスレッドが3本なので、
// "wait wait wait finish finish" と出て処理が止まる
ExecutorService service = Executors.newCachedThreadPool();
CyclicBarrier barrier = new CyclicBarrier(2);
for (int i = 0; i < 3; i++) {
    service.execute(() -> {
        try {
            System.out.print("wait ");
            barrier.await();
            System.out.println("finish ");
        } catch (BarrierBrokenException | InterruptedException ignore) {
        }
    });
}
service.shutdown();
```

#### ExecutorService の継承関係

* `Executor` … `execute(Runnabble)`
    * `ExecutorService` … `submit(Runnable)` など
        * `ThreadPoolExecutor`
        * `ForkJoinPool`
    * `ScheduledExecutorService` … `schedule(Runnable,long,TimeUnit)` 等
        * `ScheduledThreadPoolExecutor`
        
#### Executorsのメソッドとそれぞれに対応するExecutorServiceのスペック

* `newSingleThreadExecutor()`  
単一のスレッドでタスクを実行
* `newCachedThreadPool()`  
必要に応じて新規スレッドを生成、もしくは再利用してタスクを実行
* `newFixedThreadPool(int)`  
固定数のスレッドを再利用してタスクを実行
* `newScheduledThreadPool(int)`  
固定数のスレッドを再利用してタスクを実行。タスクはスケジュール可能
* `newSingleThreadScheduledExecutor()`  
単一のスレッドでタスクを実行。タスクはスケジュール可能

#### ExecutorService#execute

* `Executors` クラスの static メソッドで適切な性質の `ExecutorService` オブジェクトを取得して `execute(Runnable)` メソッドで実行
* タスクの実行状態を確認するには以下のメソッドを使う
    * `isShutdown()`  
    true なら新規タスクは受入不可（未実行か実行中のタスクがある可能性）
    * `isTerminated()`  
    true なら新規タスクは受入不可、かつ全てのタスクが終了した状態
* `isShutdown()` が true でなければ、新規タスクの `execute` が可能。true のときに `execute` すると例外が発生する

#### ExecutorService#submit

* ExecutorService に複数のオーバーロードメソッドがあり、戻り値は Future。メソッドによって Future から get できるものが異なる
    * `Future<T> submit(Runnable)`  
    タスクを実行。get できるのはタスクの成否
    * `Future<?> submit(Runnable, T)`  
    第1引数のタスクを実行し、第2引数の値を返す。get できるのはタスクの成否
    * `Future<T> submit(Callable<T>)`  
    タスクを実行。get できるのはタスクからの戻り値
* タスクはすぐ実行されるが、 `Future#get()` を実行するとタスクが終了した後も値が取得できるまで待機時間が発生する
* `Future<T>` からは `submit` したタスクのキャンセルもできる（可能な場合のみ）

#### Runnable と Callable

* `Runnable`
    * 戻り値を返さない
    * チェック例外をスローできない
    * `execute` でも `submit` でも実行できる
* `Callable`
    * 戻り値を返すことができる
    * チェック例外をスローできる
    * `submit` でしか実行できない
    
#### java.util.concurrent.Future インターフェイス

* `V get()`  
タスクの実行結果を取得できる
* `boolean cancel(boolean)`  
タスクのキャンセルを試みる
* `boolean isCancelled()`  
タスクがキャンセルされたかを知る
* `boolean isDone()`  
タスクが完了しているかどうかを知る

#### パラレルストリーム

* 生成
    * `Collection#parallelStream()`  
    コレクションからパラレルストリームを生成
* 相互変換
    * `BaseStream#parallel()`  
    シーケンシャルストリームからパラレルストリームを生成
    * `BaseStream#sequential()`  
    パラレルストリームからシーケンシャルストリームを生成
* 判定
    * `BaseStream#isParallel()`  
    自身がパラレルストリームかどうかを判定

```java
// パラレルストリームの生成
Arrays.asList(1, 2, 3).parallelStream().forEach(System.out::println);

// シーケンシャルストリームからパラレルストリームを生成
Stream.of("a", "b", "c").parallel().forEach(System.out::println);

// パラレルストリームからシーケンシャルストリームを生成
Arrays.asList("A", "B", "C").parallelStream().sequential().forEach(System.out::println);
```

#### Fork/Joinフレームワーク

※ 私はここを捨てました。真面目な方はちゃんと勉強しましょう

#### アトミック変数

* Atomic〜 で始まる名前のクラスは、自身が扱う値への操作がスレッドセーフになることを保証する
* 主なクラスは以下。Float や Double は存在しない
    * `AtomicInteger` … int型
    * `AtomicLong` … long型
    * `AtomicBoolean` … boolean型
    * `AtomicReference` … 参照型

## JDBC

#### クエリの実行

* 使用するクラスが所属するパッケージは `java.sql` と `javax.sql`
* JDBC で DB に接続するための URL フォーマットは以下の通り  
`jdbc:[DB名]//[host(:port)]/[db-name](?option)`
* 基本的な処理の流れは以下の通り
    1. `DriverManager#getConnection(url,id,pass)` で `Connection` 取得
    2. `Connection#createStatement()` で `Statement` 取得
    3. `Statement#executeQuery(sql)` で `ResultSet` 取得
    4. `ResultSet` からクエリの結果を取り出す
* JDBC3.0 以前では `DriverManager#getConnection` の前に `Class.forName([JDBCドライバクラス名])` を実行する必要があった
* `executeQuery` で実行するクエリの結果が0件でも戻り値は null ではなく、空の `ResultSet` になる
* 1つの `Statement` で扱える `ResultSet` は1つだけ。最初の `ResultSet` を閉じずに別の `ResultSet` を取得すると、最初のは自動的に閉じられる。閉じられた `ResultSet` を操作しようとすると例外  
`executeUpdate` などを実行したときも自動で閉じる

#### Statementにあるメソッドの使い分け

* `ResultSet executeQuery(String sql)`  
クエリの実行結果を返す
* `int executeUpdate(String sql)`  
INSERT/UPDATE/DELETEなどのSQLを実行し、処理件数を返す
* `boolean execute(String sql)`  
クエリを含む、あらゆる種類の SQL を実行して処理の結果として `ResultSet` が得られたかを返す。true なら `ResultSet` が返ってきており、false ならそうではない  
処理の結果 `ResultSet` が返ってきたなら `Statement#getResultSet()` で取得する。 `ResultSet` でなければ `Statement#getUpdateCount()` で処理件数を取得する

#### ResultSet の取得と性質

* `Connection#createStatement(type, concurrency)` で `ResultSet` の「スクロール可否」と「テーブルデータの更新可否」を順番に指定する
* スクロール可否は以下の3種類で、全て `ResultSet` クラスの定数
    * `TYPE_FORWARD_ONLY` … 順方向にのみ移動可
    * `TYPE_SCROLL_INSENSITIVE` … 双方向に移動可。カーソルへの変更は反映しない
    * `TYPE_SCROLL_SENSITIVE` … 双方向に移動可。カーソルへの変更を反映する
* テーブルデータの更新可否は以下の2種類で、全て `ResultSet` クラスの定数
    * `CONCUR_READ_ONLY` … 読み込みのみ
    * `CONCUR_UPDATABLE` … 更新可能
* `Statement#createStatement(..., ResultSet.CONCUR_UPDATABLE)` で更新可能な `ResultSet` を取得する時、更新対象とするカラムを `executeQuery` の select 文に指定しておかなくてはならない

#### ResultSet のスクロール用メソッド

* `boolean absolute(int)`  
指定した行（絶対位置を指定）へ移動。先頭行は1。0はbeforeFirst
* `boolean relative(int)`  
相対位置を指定して移動。正の値なら次の行へ、負の値なら前の行にゆく
* `boolean next()`  
ひとつ次の行に移動
* `boolean previous()`  
ひとつ前の行に移動
* `boolean first()`  
先頭行に移動
* `void beforeFirst()`  
先頭行の直前に移動
* `boolean last()`  
最終行に移動
* `void afterLast()`  
最終行の直後に移動

#### 更新可能な ResultSet

* 更新  
`updateString` や `updateInt` で変更し `updateRow` で変更を確定させる  
`Statement` のタイプが `TYPE_SCROLL_INSENSITIVE` でも `updateRow` しない限り、DB の中身どころか結果セットさえ変更されない
    * `updateString(int, String)`
    * `updateInt(int, int)`
    * `updateRow()`
* 挿入  
`moveToInsertRow` で挿入行に移動し `updateString` や `updateInt` で挿入内容を指定して `insertRow` で挿入する
    * `moveToInsertRow()`
    * `insertRow()`
* 削除  
現在行を削除する
    * `deleteRow()`

## ローカライズとフォーマット

#### java.util.Locale オブジェクトの取得

1. `Locale#getDefault()`  
Java実行環境のロケール
2. `new Locale(String)`  
引数は「言語コード」
3. `new Loacle(String, String)`  
引数は「言語コード, 国コード」
4. `new Loacle(String, String, String)`  
引数は「言語コード, 国コード, バリアント」
5. new で生成した `Locale.Builder` に適切な値を設定して `build()` で生成

#### java.util.Properties

* サポートするファイルフォーマットはテキストとXML
* テキストファイルの場合、キーと値を = もしくは : で区切って列挙する
* ファイルからプロパティリストをロード
    * `load(InputStream)`
    * `load(Reader)`
    * `loadFromXML(InputStream)`
* プロパティリストを出力
    * `list(OutputStream)`
    * `list(Writer)`
    * `entrySet()`
    * `forEach(BiConsumer)`
* プロパティの取得
    * `getProperty(String)`  
    該当するKeyが無ければ null を返す
    * `getProperty(String, String)`  
    該当するKeyが無ければ第2引数を返す
  
#### java.util.ListResourceBundle

* リソースを .class ファイルで用意してクラスパスが張られている場所に置く
* 使い方は以下の通り
    1. `ListResourceBundle` を継承した public クラスを定義  
    `public Object[][] getContents()` をオーバーライド  
    このクラスの完全修飾名（パッケージ名＋クラス名）が基底名に、このクラスがデフォルトロケール用になる  
    例：クラス名は "MyResource" とする
    2. 1とは異なるロケール用のクラスを `ListResourceBundle` を継承して定義する  
    ネーミングルールは **「基底名_言語コード_国コード」**  
    例：英語ロケール用のクラス名は "MyResource_en_US" となる
    3. `ResourceBundle#getBundle(基底名[, ロケール])` で `ListResourceBundle` オブジェクトを取得
    4. `ResourceBundle#getXXXXX(キー文字列)` で値を取り出す

#### java.util.PropertyResourceBundle

* リソースを .properties ファイルで用意してクラスパスが張られている場所に置く
* 使い方は以下の通り
    1. デフォルトロケール用のプロパティファイルを作成
        * 拡張子は **.properties**
        * 『キー名 = 値』のフォーマットで複数のプロパティを定義
        * ファイルは ISO-8859-1 エンコードされている必要がある
        * ファイルの命名規則は `ListResourceBundle` のクラス名と同じ
    2. 1とは異なるロケール用のファイルを作成
        * ファイルの命名規則は `ListResourceBundle` のクラス名と同じ
        * 英語ロケール用なら MyResource_en_US.properties となる
    3. `ResourceBundle#getBundle(基底名[, ロケール])` で `PropertyResourceBundle` オブジェクトを取得
        * `ListResourceBundle` を取得するのと同じメソッド
    4. 以下のメソッドで値を取り出す。getInt はない
        * `ResourceBundle#getObject(キー文字列)`
        * `ResourceBundle#getString(キー文字列)`
        * `ResourceBundle#getStringArray(キー文字列)`

#### 使用されるリソースバンドルの優先順位

* ロケールに対応するリソースバンドルが存在しなければ `MissingResourceException` 例外が発生する
* 同名のリソースバンドルが複数あっても例外にはならない
* ファイル単位ではなく個々のプロパティ単位でリソースバンドルが検索される。検索の優先順位は以下の通り
    1. 言語コード、国コードが一致
    2. 言語コードが一致
    3. デフォルトロケールが一致
* 指定したキーに対応するプロパティがどのクラス／ファイルにも存在しなければ `MissingResourceException` が発生する
* `ListResourceBundle` クラスとプロパティファイルの両方が同じ名前で存在すると、ファイルよりクラスのほうが優先される

#### NumberFormatを使って数値をフォーマット

* `NumberFormat` クラスの static メソッドを使って目的に応じたオブジェクトを取得
    * `getInstance()`  
    デフォルトロケールの数値フォーマット
    * `getInstance(Locale)`  
    任意のロケールの数値フォーマット
    * `getCurrencyInstance(Locale)`  
    任意のロケールの通貨フォーマット
    * `getIntegerInstance(Locale)`  
    任意のロケールの整数フォーマット
* `format(long)` もしくは `format(double)` でフォーマット済み文字列を取得
* `parse(String)` で文字列から数値を取得。戻り値は `Number`

# 更新履歴

* 2020/08/23  
Qiita に公開
* 2020/08/24  
関数型インターフェイスの一覧を表形式に変更  
列挙値に関するサンプルコードを追加