1Z0-809-JPN - Java SE 8 Programmer II を受験しました。
結果: 合格 / 正解率 72% (合格ライン: 65%)
という訳で『Oracle Certified Java Programmer, Gold SE 8 認定資格』を取得しました。
「中上級者向け資格」とあるので、Java の開発案件を数年程度やっていれば問題の内容は十分理解できると思います。
とは言っても、出題範囲は広く覚えることが多いため、ある程度の勉強は必要かと思います。今回はこの問題集で勉強しました。
徹底攻略 Java SE 8 Gold 問題集[1Z0-809]対応
ひと通り目を通して模擬試験2回分をやってみましたが、実際の試験は問題集の内容より少し難易度が高いように感じました。
出題範囲も広く全体的にバランスよく出題されていると思います。また、問題集には載っていない引数違いのオーバーロードメソッドが出題されていたりするため、勉強するときは API ドキュメントを参考にひと通りの用途はおさえておくとよいです。
本当は正解率90%以上を叩き出してドヤァしたかったところですが、割とギリギリの合格になってしまいました...。
1Z0-808-JPN - Java SE 8 Programmer I を受験したときのメモはこちら。
kntmr.hatenablog.com
以下、個人的なメモ。
継承は is-a 関係 (A は B である) あるいは kind-of-a 関係 (A は B の一種である) を構成する。has-a 関係 (A は B を持っている) は、ある型のフィールドで別の型を参照する関係であり、集約 (Aggregation) や構成集約 (Composition) と呼ばれる。
ポリモーフィズムは、コンパイル時に決定する単一の型 (静的な型) で実行時に複数の型 (動的な型) を扱う仕組み。ポリモーフィックな型は is-a 関係で構成される。is-a 関係は、インタフェースの実装やクラス/インタフェースの継承で実現する。
静的ポリモーフィズム (Static Polymorphism) は、単一の型におけるオーバーロードで実現する。動的ポリモーフィズム (Dynamic Polymorphism) は、複数の型におけるオーバーライドで実現する。
以下の場合、左辺の Foo はコンパイル時に決定する静的な型、右辺の Foo, Bar は実行時に決定する動的な型。
class Foo {}
class Bar extends Foo {}
Foo foo = new Foo();
Foo foo = new Bar();
スーパークラスのメソッドをオーバーライドする場合、アクセス修飾子を制限が厳しいものに変更することはできない。
インタフェースは型や振る舞いの仕様を規定する。インタフェースで宣言するメソッドは暗黙的に public となる。フィールドは public static final となる。
抽象クラスはインタフェースの役割に加えて実装を提供することができる。継承関係において関連するクラス間の共通の実装をまとめる。
クラスの多重継承はできない。ただし、インタフェースの多重継承と default メソッドを組み合わせることで、複数のクラスが提供する実装を再利用することができる。
hashCode
メソッドはオブジェクトの識別に使用する int 型のハッシュ値を生成して返す。ハッシュ値はオブジェクト検索のパフォーマンス向上の目的で使用される。Object クラスの hashCode
メソッドはメモリアドレスをもとに値を生成して返す。
hashCode
と equals
の関係は以下の通り。
equals
メソッドで等しいとされるオブジェクトは必ず同じハッシュ値を返す
equals
メソッドで等しくないとされるオブジェクトであっても異なるハッシュ値を返す必要はない (可能な限り異なるハッシュ値を返すことが望ましい)
equals
メソッドの判定で使われるフィールドの値が変更されない限りは必ず同じハッシュ値を返す
インタフェースの static メソッドは、インタフェース型参照からのみ呼び出し可能。default メソッドは、インタフェースのみで宣言可能な具象メソッドであり、実装クラスのインスタンス参照から呼び出し可能。
シグニチャが同一の default メソッドを持つ複数のインタフェースを実装した場合はコンパイルエラーになる。コンパイルエラーを回避するには実装クラスで該当のメソッドをオーバーライドする。ただし、型階層の距離が異なる場合はコンパイルエラーにならず、階層の距離が近い方の default メソッドが呼ばれる。
入れ子クラスには、クラスのメンバーとして宣言されるメンバークラス、static メンバークラス、メソッド内で宣言されるローカルクラスがある。メンバークラスとローカルクラスは内部クラスと呼ばれる。内部クラスは匿名クラスとして利用できる。メンバークラスにはアクセス修飾子を付けることができる。
インタフェース内に宣言されたメンバークラスは暗黙的に static メンバークラスとなる。
内部クラスのインスタンスを static メソッドから利用するには、外部クラスのインスタンス参照が必要。外部クラスのインスタンスを生成して、その参照を用いて内部クラスのインスタンスを生成する。(new Outer().new Inner()
)
内部クラスの private フィールドには、その内部クラスが宣言されているクラスのメンバーメソッドから直接アクセスできる。
サブクラスのコンストラクタの先頭には、スーパークラスのデフォルトコンストラクタを呼び出すコード (super()
) がコンパイラによって挿入される。スーパークラスにデフォルトコンストラクタが存在しない場合、スーパークラスのコンストラクタを呼び出すコード (super(args)
など) を明示的に記述する必要がある。
列挙型は暗黙的に Enum クラスのサブクラスとしてコンパイルされる。列挙型のコンストラクタは暗黙的に private となる。
型パラメータは、アノテーション、型変数、型境界の要素から構成される。(アノテーションと型境界は省略可)
総称型を利用する場合には、型変数に対して実際に利用する型を割り当てる必要がある。割り当てる型を型引数 (Type Arguments) と呼ぶ。型引数に指定できるのは配列型を含む参照型のみ。
型引数が割り当てられた総称型をパラメータ化された型 (Parameterized Types) と呼ぶ。
- static 修飾子付きの型変数は宣言不可 (
static T t
)
- 型変数のインスタンス生成は不可 (
T obj = new T()
)
- 型変数を要素の型に指定した配列生成は不可 (
T[] array = new T[3]
)
- instanceof 演算子では利用不可 (
t instanceof T
)
- 型変数に対する .class 参照は不可 (
T.class
)
ジェネリックメソッドは戻り値の記述の前に型パラメータを宣言する。
public <T> void foo1(T t) {}
public <T> T foo2() {}
型パラメータの宣言では、extends キーワードを利用して型変数が取り得る型に制約を課すことができる。これを型境界 (Type Bound) と呼ぶ。
class Foo<T extends Number> {}
型境界に指定する型には宣言している他の型変数が使用できる。
class Foo<T, U extends T> {}
総称型をパラメータ化する際の型引数にはワイルドカードを記述することができる。型変数にはワイルドカードは記述できない。
- 型パラメータリストにおける型変数には記述できない (
class Foo<?> {}
)
- インスタンス生成のコードには記述できない (
Foo<?> obj = new Foo<?>()
)
ワイルドカードに対しては2種類の型境界を指定することができる。<?>
は <? extends Object>
と等価。
<? extends T>
: 型境界の上限をT型に制限する。?
が取り得る型はTかTのサブクラスとなる。(上限境界 / Upper Bound)
<? super T>
: 型境界の下限をT型に制限する。?
が取り得る型はTかTのスーパークラスとなる。(下限境界 / Lower Bound)
総称型を継承または実装する場合は、型引数を指定して総称型をパラメータ化する。
class Foo<T> {}
class Bar<T> extends Foo<T> {}
class Bar extends Foo<String> {}
class Bar<T> extends Foo<String> {}
java.util.List<E>
インタフェースは、インデックスによって順序付けられるデータ構造を表すコレクションインタフェース。
ArrayList<E>
クラスは、内部で配列を利用した List<E>
インタフェースの実装クラス。ランダムアクセスは高速。要素の追加は、追加する位置が先頭に近いか、要素数が多いほどパフォーマンスが劣化する。
LinkedList<E>
クラスは、双方向連結リストと呼ばれる。ランダムアクセスでは、先頭または末尾からリンクを辿って目的の要素を探すため、ArrayList に比べるとパフォーマンスは劣る。要素の追加は、要素の前後のリンクを張り直すだけなので、ArrayList に比べてパフォーマンスは優れる。
ArrayList クラスや LinkedList クラスはスレッドアンセーフ。
その他、Vector<E>
クラスや CopyOnWriteArrayList<E>
クラスなどの実装クラスが存在する。
Arrays クラスの asList
メソッドが返す List オブジェクトは、Arrays クラス内部で宣言されている private static な ArrayList クラスのインスタンスであり、java.util.ArrayList
と型の互換性はない。要素の追加や削除はできない。
java.util.Queue<E>
インタフェースは、FIFO のデータ構造を表す。
BlockingQueue インタフェースは、Queue インタフェースのサブインタフェースで、要素の挿入や削除においてブロッキングする機能を提供する。キューが満杯の場合は空きができるまで待機する。BlockingQueue インタフェースの実装クラスには、ArrayBlockingQueue クラス、PriorityBlockingQueue クラス、DelayQueue クラスなどが存在する。
java.util.Deque<E>
インタフェースは、Queue インタフェースのサブインタフェースで、両端から要素を挿入/削除できるデータ構造を表す。Deque インタフェースの実装クラスには、ArrayDeque<E>
クラス、LinkedList<E>
クラスなどが存在する。Deque インタフェースはインデックスによる要素アクセスは非サポート。
ArrayDeque<E>
クラスは内部で配列を使用する。null は要素として格納できない。FIFO として使う場合には LinkedList<E>
クラスより高速。FILO として使う場合には java.util.Stack<E>
クラスより高速。
ArrayDeque クラスや LinkedList クラスはスレッドアンセーフ。Stack クラスはスレッドセーフ。
java.util.Set<E>
インタフェースは、重複する要素を持たないことを保証するコレクションインタフェース。重複する要素とは、equals
メソッドで等しいとされる要素を指す。含まれる要素の順序は保証しない。
Set インタフェースの実装クラスである HashSet<E>
クラスは、要素の管理にハッシュテーブルを使用する。TreeSet<E>
クラスは、木構造に基づく実装であり、要素の順序付けをサポートする。
java.util.Map<K, V>
インタフェースは、一意なキーとキーに紐付けられた値のペアを要素として扱うデータ構造を表す。コレクションフレームワークに含まれているが、Collection<E>
インタフェースのサブインタフェースではない。
Map.Entry<K, V>
インタフェースは、Map オブジェクトに格納される1要素を表す。Map インタフェースの entrySet
メソッドで取得する。
Map.Entry<K, V>
インタフェースが提供する comparingByValue(Comparator<? super V>)
メソッドは、Map オブジェクトの値でソートする。値が同じ場合はキーでソートする。String.CASE_INSENSITIVE_ORDER
は String クラスが提供する Comparator<String>
オブジェクトで、大文字や小文字は区別しない。
java.lang.Comparable<T>
インタフェースを実装するクラスのオブジェクトは、自身と他のオブジェクトを比較することができる。Comparable<T>
インタフェースでは、compareTo(T o)
メソッドが宣言されている。自身が大きい場合は正の int 値、自身が小さい場合は負の int 値、等しい場合は 0 を返す。
java.lang.Comparator<T>
インタフェースを実装するクラスのオブジェクトは、他の2つのオブジェクトを比較する機能を実装する。Comparator<T>
インタフェースでは、compare(T o1, T o2)
メソッドが宣言されている。o1 > o2 の場合は正の int 値、o1 < o2 の場合は負の int 値、o1 == o2 の場合は 0 を返す。
TreeMap<K, V>
クラスは、Map<K, V>
インタフェースの実装クラスで、デフォルトではキーの自然順序付け (昇順) で要素を並び替える。
TreeSet<E>
クラスは、内部で要素を並び替える際に格納された要素を Comparable<T>
型にキャストするため、Comparable<T>
を実装していないオブジェクトを TreeSet などのソート機能付きのコレクションに格納すると ClassCastException がスローされる。
List<E>
インタフェースの default メソッドで宣言されている replaceAll
メソッドは、引数に指定された UnaryOperator<E>
オブジェクトの実装に従ってリスト内の要素を計算して置換する。
ラムダ式と組み込み関数型インタフェース
関数型インタフェースの要件は、抽象メソッドが1つ宣言されていること。SAM (Single-Abstract-Method)) と呼ばれる。default メソッドや static メソッドの宣言は関数型インタフェースの要件に関係しない。関数型インタフェースの妥当性は java.lang.@FuntionalInterface
でコンパイル時にチェックすることができる。
すべてのインタフェースの実装クラスは Object クラスのサブクラスであるため、Object クラスで宣言されているメソッドと同一シグニチャの抽象メソッドは関数型インタフェースの抽象メソッドとは見なされない。
@FuntionalInterface
public interface Foo {
void hoge();
boolean equals(Object obj);
}
@FuntionalInterface
public interface Bar {
boolean equals(Object obj);
}
ラムダ式は、匿名内部クラスの実装を簡潔に記述するための構文仕様。(厳密にはラムダ式と匿名クラスの挙動は違う)
関数型プログラミングでは、関数を第1級オブジェクトとして扱うことができる。第1級オブジェクトとはリテラルと同様に扱うことができるオブジェクトを指す。
- 変数に対して関数を代入できる
- 関数の引数に関数を指定できる
- 関数の戻り値に関数を返せる
特に、2, 3 をサポートする関数を高階関数と呼ぶ。
引数を取るラムダ式では引数の型は省略可。引数が1つだけの場合は引数を囲う括弧は省略可。括弧を省略する場合は引数の型は記述不可。引数を2つ以上取るラムダ式では括弧は省略不可。戻り値を返すラムダ式でブロックを省略している場合は return キーワードは記述不可。
java.util.function
パッケージが提供するインタフェースは基本的に以下の4つ。
Supplier<T>
: 引数なしでT型の値を返す T get()
を宣言する関数型インタフェース
Predicate<T>
: T型の値を引数に取って評価する boolean test(T)
を宣言する関数型インタフェース
Consumer<T>
: T型の値を引数に取って何らかの処理を実行する void accept(T)
を宣言する関数型インタフェース
Function<T, R>
: T型の値を引数に取ってR型の値を返す R apply(T)
を宣言する関数型インタフェース
基本のインタフェースの他に特殊化したインタフェースが存在する。
BiFunction<T, U, R>
: Function<T, R>
の特殊化型で、引数を2つ取る R apply(T, U)
を宣言する関数型インタフェース
UnaryOperator<T>
: T型の引数を取ってT型の値を返すインタフェースで、Function<T, T>
のサブインタフェース (単項演算子)
BinaryOperator<T>
: T型の値を2つ取ってT型の値を返すインタフェースで、BiFunction<T, T, T>
のサブインタフェース (二項演算子)
特殊化型を含めて、ひと通りのインタフェースについては以下にまとめる。
java.util.function パッケージのインタフェースサマリー (用途別) - kntmr-blog
Predicate<T>
インタフェースの default メソッドとして、論理積 (AND) を取る and
メソッド、論理和 (OR) を取る or
メソッド、論理否定 (NOT) を取る negate
メソッドが提供されている。
Function インタフェースの default メソッドとして、andThen
メソッドと compose
メソッドが提供されている。andThen
メソッドは引数に指定された Function オブジェクトを後に適用する。compose
メソッドは引数に指定された Function オブジェクトを先に適用する。
関数型インタフェースの抽象メソッドとラムダ式で呼び出すメソッドのシグニチャが同一である場合はメソッド参照が使える。メソッド参照を使う場合はラムダ式の仮引数宣言とメソッド呼び出しの括弧は記述不可。
以下の抽象メソッド void accept(T)
メソッドは System.out.println
メソッドとシグニチャが同じである。ラムダ式で処理を実装する場合は以下の通り。
Consumer<String> c = s -> System.out.println(s);
c.accept("foo");
これをメソッド参照で記述する場合は以下の通り。
Consumer<String> c = System.out::println;
c.accept("foo");
コンストラクタメソッドの呼び出しをメソッド参照で記述する場合はコンストラクタ参照と呼ばれる。コンストラクタ参照の代入先の変数は、関数型インタフェースである必要がある。
以下の抽象メソッド R apply(T)
メソッドはコンストラクタメソッドと同じシグニチャである。ラムダ式で記述する場合は以下の通り。
Function<String, Integer> f = s -> new Integer(s);
Integer i = f.apply("1");
これをコンストラクタ参照で記述する場合は以下の通り。
Function<String, Integer> f = Integer::new;
Integer i = f.apply("1");
コンストラクタ参照である Foo::new
は () -> new Foo()
というラムダ式に置き換えて考えることができる。配列オブジェクトを生成するコンストラクタ参照は、データ型[]::new
という構文で記述する。
引数として受け取ったオブジェクトの引数を取らないメソッド呼び出しをメソッド参照で書く場合、クラス名::メソッド名
の形式で記述することができる。以下はラムダ式の引数で受け取った String オブジェクトの引数なしのメソッドを呼び出す場合。
Function<String, Integer> f = s -> s.length();
System.out.println(f.apply("foo"));
これをメソッド参照で記述する場合は以下の通り。
Function<String, Integer> f = String::length;
System.out.println(f.apply("foo"));
インスタンス変数で参照するオブジェクトのメソッド呼び出しをメソッド参照で書く場合は インスタンス変数名::メソッド名
の形式で記述できる。
String s1 = "foo";
Function<String, String> f = s2 -> s1.concat(s2);
System.out.println(f.apply("bar"));
これをメソッド参照で記述する場合は以下の通り。
String s = "foo";
Function<String, String> f = s::concat;
System.out.println(f.apply("bar"));
Stream API
ストリームAPIの基本的なステップは以下の通り。
- データソースからストリームオブジェクトを取得する
- ストリームオブジェクトに対して中間操作を適用する
- ストリームオブジェクトに対して終端操作を適用する
データソースは、配列やコレクション、ファイルから読み込んだデータ、数値範囲などが挙げられる。
中間操作では処理関数を保持した新しいストリームオブジェクトが返る。中間操作のメソッド呼び出しの時点ではストリームオブジェクトに対する処理は実行されない。中間操作の処理が実行されるのは終端操作が実行されるタイミング。(遅延評価)
終端操作はストリームオブジェクトに対して1度だけ適用できる。終端操作が適用されたストリームに対して再度ストリーム処理を適用することはできない。
java.util.stream
パッケージでは、BaseStream<T, S extends BaseStream<T, S>>
インタフェースを基底とした以下のサブインタフェースが提供されている。
Stream<T>
インタフェース
IntStream
インタフェース
LongStream
インタフェース
DoubleStream
インタフェース
Stream<Integer>
と IntStream
に互換性はない。
BaseStream<T, S extends BaseStream<T, S>>
インタフェースは AutoCloseable インタフェースを継承しているため、try-with-resources 文を使用してリソースを自動的に close できる。特にファイルを処理する場合には try-with-resources 文を使用して必ず close すること。
java.util.Collection
インタフェースでは、default メソッドとして stream
メソッドが提供されている。
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream = list.stream();
java.lang.Iterable
インタフェースでは、default メソッドとして forEach
メソッドが提供されている。Iterable インタフェースを継承する Collection インタフェースの実装クラスは Stream オブジェクトを取得することなく forEach
メソッドが使える。
Arrays.asList("A", "B", "C").forEach(System.out::print);
Map<K, V>
インタフェースでは、default メソッドとして forEach
メソッドが提供されている。引数には BiConsumer オブジェクトを指定する。Map インタフェースでは stream
メソッドは提供していない。代わりに entrySet
メソッドで Set オブジェクトを取得して stream
メソッドを呼び出す。
map.entrySet().stream()
配列からストリームオブジェクトを取得する場合は java.util.Arrays
クラスの static メソッドである stream
メソッドを使う。
Stream<String> stream = Arrays.stream(array);
stream
には配列の範囲を指定するオーバーロードメソッドが存在する。範囲指定では、開始インデックスの値は含むが、終了インデックスの値は含まない。同様に、range
メソッドも開始インデックスの値は含むが、終了インデックスの値は含まない。rangeClosed
メソッドは開始インデックスと終了インデックスの値を含む。
IntStream.range(1, 5).forEach(System.out::print);
IntStream.rangeClosed(1, 5).forEach(System.out::print);
任意の参照型オブジェクトの集合から Stream オブジェクトを取得する場合は Stream インタフェースの static メソッドである of
メソッドを使う。
Stream<String> stream = Stream.of("a", "b", "c");
BufferedReader クラスの lines()
メソッドと Files クラスの lines(Path)
メソッドは、テキストファイルの内容を読み込んで Stream<String>
オブジェクトとして返す。
ストリーム内の要素を並べ替える場合は Stream インタフェースの sorted
メソッドを使う。
Arrays.asList(2, 3, 1).sorted().forEach(System.out::print);
Arrays.asList(2, 3, 1).sorted((i, j) -> j - i).forEach(System.out::print);
peek
メソッドは元のストリームオブジェクトを返す中間操作メソッド。
Predicate オブジェクトを引数に取り、boolean 型の戻り値を返す条件判定用の終端メソッドは以下の3つ。
anyMatch
メソッド (1つでも条件に合致する要素があれば、その時点で true を返す)
allMatch
メソッド (1つでも条件に合致しない要素があれば、その時点で false を返す)
noneMatch
メソッド (1つでも条件に合致する要素があれば、その時点で false を返す)
IntStream, LongStream, DoubleStream の 条件判定用のメソッドでは、それぞれ IntPredicate, LongPredicate, DoublePredicate を引数に取る。Predicate オブジェクトは引数に取らない。
引数を取らず、Optional<T>
オブジェクトを返す探索用の終端メソッドには、findAny
メソッドと findFirst
メソッドがある。要素がない場合は空の Optional オブジェクトを返す。
Stream<T>
インタフェースでは、入れ子構造のストリームを平坦なストリームに変換する中間操作メソッドとして、flatMap
メソッドが提供されている。その他、プリミティブ型に対応するプリミティブバージョンの flatMapToXxx
メソッドが提供されている。(IntStream, LongStream, DoubleStream を返す)
Stream オブジェクトをプリミティブバージョンの Stream オブジェクトに変換する場合は、Stream インタフェースの mapToXxx
メソッドを使用する。
java.util.Map<K, V>
インタフェースでは、default メソッドとして merge(K, V, BiFunction)
メソッドが提供されている。指定したキーの値が存在しない場合、第2引数の value が設定される。指定したキーの値が存在する場合、第3引数の BiFunction オブジェクトの処理結果が値として設定される。引数の value や BiFunction の戻り値が null の場合、キーのエントリは削除される。
ストリームオブジェクトの要素を1つに要約する終端操作をリダクションと呼ぶ。一般的なリダクション操作には、合計や平均の算出、最大値、最小値の取得などがある。
java.util.stream.Stream
インタフェースの min
メソッドおよび max
メソッドの引数には Comparator<? super T>
型のオブジェクトを指定する。メソッドの戻り値は Optional 型。
System.out.println(Stream.of("a", "b", "c").count());
System.out.println(IntStream.of(1, 2, 3).average().getAsDouble());
System.out.println(IntStream.of(1, 2, 3).max().getAsInt());
任意のリダクション処理を実装する場合は reduce
メソッドを使う。引数に BinaryOperator<T>
オブジェクトを取る reduce
メソッドは Optional 型を返す。
可変リダクション操作とは、何らかの可変コンテナに要素を収集する操作のこと。collect
メソッドを使う。可変コンテナには List や Map, StringBuilder などがある。collect
メソッドの引数には Collector オブジェクトを指定する。
ユーティリティクラスである java.util.Collectors
クラスは、一般的によく使われる可変リダクション操作を実装した Collector オブジェクトを返す static メソッドを提供している。
counting
メソッド : 要素の数をカウントする Collector オブジェクトを返す
groupingBy
メソッド : 指定された Function オブジェクトに従って要素をグループ化して Map オブジェクトに格納する Collector オブジェクトを返す
joining
メソッド : 要素を連結して1つの String オブジェクトにする Collector オブジェクトを返す
maxBy
メソッド : 指定された Comparator オブジェクトに従って最大要素を返す Collector オブジェクトを返す
minBy
メソッド : 指定された Comparator オブジェクトに従って最小要素を返す Collector オブジェクトを返す
reducing
メソッド : 指定された BinaryOperator オブジェクトに従って要素のリダクションを実行する Collector オブジェクトを返す
toList
メソッド : 要素を新しい List に蓄積する Collector オブジェクトを返す
toSet
メソッド : 要素を新しい Set に蓄積する Collector オブジェクトを返す
以下はプリミティブ特殊化された関数型インタフェースを引数に取る。Collectors.averagingXxx
メソッドや Collectors.summarizingXxx
メソッドも同様。
Collectors.summingInt(ToIntFunction<? super T>)
Collectors.summingLong(ToLongFunction<? super T>)
Collectors.summingDouble(ToDoubleFunction<? super T>)
Collectors.partitioningBy(Predicate<? super T>)
は引数の Predicate オブジェクトの判定に従って要素を分割して Map<Boolean, List<T>>
オブジェクトで返す。
Comparator インタフェースの comparing
メソッドで Comparator オブジェクトを簡易的に取得できる。comparing
メソッドの引数にはソートに使用するキーを返す Function オブジェクトを指定する。もしくは、Function インタフェースの identity
メソッドで入力引数を返す関数を返すメソッドを使う。
List<Integer> list = Arrays.asList(7, 2, 5);
list.stream().min(Comparator.comparing(n -> n)).get();
list.stream().min(Comparator.comparing(Function.identity())).get();
例外の multi-catch 文の catch ブロックに継承関係にある例外型を書くとコンパイルエラーとなる。multi-catch ブロックの引数に宣言した変数は暗黙的に final となる。
try-with-resources 文では catch ブロックや finally ブロックは必須ではない。try-with-resources 文に宣言できるクラスは、AutoCloseable インタフェースか、サブインタフェースである Closeable インタフェースを implements して、抽象メソッドである public void close()
メソッドを実装する必要がある。
try ブロックの中で例外が発生した場合は close
メソッドが呼ばれてから catch ブロックに入る。try-with-resources 文で close
メソッドが呼ばれる順番はインスタンスを生成した逆順。
メソッドをオーバーライドする際、throws 宣言する例外クラスを省略したり、元の例外クラスのサブクラスを throws に宣言できる。別の例外クラスや元の例外クラスのスーパークラスを throws に宣言することはできない。
アサーションの基本構文は以下の通り。assert 文の評価が false の場合は AssertionError
クラスがスローされる。
assert x == 1;
assert (y == 1);
assert x == 1 : "error message";
アサーション機能はデフォルトでは無効。クラスやパッケージごとに有効無効を指定できる。
java -ea Foo
java -ea com.package
java -da Bar
java -da com.package
java.time
パッケージが提供する。ISO 8601 をベースに設計されている。
時差情報を含まない現地日時をローカルタイム (Local Time) と呼ぶ。
- LocalDate クラス : 日付のみを扱う
- LocalTime クラス : 時刻のみを扱う
- LocalDateTime クラス : 日付と時刻を扱う
時差情報を含む日時をオフセットタイム (Offset Time) と呼ぶ。
- OffsetTime クラス : 時刻のみを扱う
- OffsetDateTime クラス : 日付と時刻を扱う
タイムゾーンによる時差を扱うクラス。
- ZonedDateTime クラス : 地域情報を含む日付と時刻を扱う
現在日時を表すインスタンスを取得する場合は以下の通り。
LocalDate.now();
LocalTime.now();
LocalDateTime.now();
特定の日付を表すインスタンスを取得する場合は以下の通り。
LocalDate.of(2016, 12, 30);
LocalDate.of(2016, Month.DECEMBER, 30);
年、年月、月日を個別に扱うクラス。of(int)
メソッドや from(TemporalAccessor)
メソッドなどのファクトリメソッドで取得する。
- Year クラス : 年を表す
- YearMonth クラス : 年月を表す
- MonthDay クラス : 月日を表す
java.time
パッケージには、月を表す Month 列挙型 (Month.JANUARY
など) と曜日を表す DayOfWeek 列挙型 (DayOfWeek.MONDAY
など) が存在する。
日時を表現するクラスは、java.time.temporal
パッケージの TemporalAccessor インタフェースや、そのサブインタフェースである Temporal インタフェースを実装する。
TemporalAccessor は読み取り専用のメソッドを宣言している。Temporal は読み書きのメソッドを宣言している。Date and Time API の日時を表すクラスは不変であるため、書き込みメソッドを呼び出した場合はコピーオブジェクトが返る。
日時を表す TemporalAccessor 型のオブジェクトから別の日時を表すオブジェクトを取得する場合は from
メソッドを利用する。
LocalDate.from(LocalDateTime.now());
LocalTime.from(LocalDateTime.now());
以下は、DateTimeException がスローされる。
LocalDateTime.from(LocalDate.now());
LocalDateTime.from(LocalTime.now());
OffsetDateTime や ZonedDateTime から LocalDateTime オブジェクトを取得することは可能。時差情報やタイムゾーンの情報が失われるだけで例外はスローされない。
文字列形式の日付から LocalDate インスタンスを生成する場合は parse
メソッドを利用する。
LocalDate クラスの withXxx
メソッド (withYear(int)
, withMonth(int)
など) は、日時の値を変更した LocalDate オブジェクトを生成して返す。with(TemporalField, long)
メソッドは、指定されたフィールドに新しい値を設定した LocalDate オブジェクトを生成して返す。
LocalTime クラスの withXxx
メソッド (withHour(int)
, withMinute(int)
など) は、時間の値を変更した LocalTime オブジェクトを生成して返す。with(TemporalField, long)
メソッドは、指定されたフィールドに新しい値を設定した LocalTime オブジェクトを生成して返す。
LocalDateTime クラスも同様。
java.time.temporal
パッケージの TemporalUnit インタフェースを実装する ChronoUnit 列挙型は、1ヶ月や1年など日時の単位を表す。
ChronoUnit.DECADES
: 10年
ChronoUnit.CENTURIES
: 100年
ChronoUnit.MILLENNIA
: 1000年
ChronoUnit.ERAS
: 1紀元
- など
ChronoUnit には簡単な計算メソッドが定義されている。
Temporal t = ChronoUnit.DAYS.addTo(day, 5);
long days = ChronoUnit.DAYS.between(start, end);
java.time.temporal
パッケージの TemporalField インタフェースを実装する ChronoField 列挙型は、年や月など日時のフィールドを表す。
ChronoField.YEAR
: 年
ChronoField.MONTH_OF_YEAR
: 月
- など
現在のタイムゾーンは java.time.ZoneId
クラスの static メソッドである systemDefault
メソッドで取得する。
System.out.println(ZoneId.systemDefault());
java.time.Duration
クラスは時間ベースの時間量を表す。java.time.Period
クラスは日付ベースの時間量を表す。間隔を取得するための static な between
メソッドが宣言されている。
日時の表示形式をフォーマットする場合は java.time.format
パッケージの DateTimeFormatter クラスを利用する。DateTimeFormatter クラスはスレッドセーフ。SimpleDateFormat クラスはスレッドアンセーフ。
DateTimeFormatter クラスはコンストラクタを提供していない。基本的なフォーマッタは static フィールドに宣言されている。
DateTimeFormatter.BASIC_ISO_DATE
: 基本的なISO日付
DateTimeFormatter.ISO_DATE
: オフセット付きまたはオフセットなしのISO日付
DateTimeFormatter.ISO_LOCAL_DATE
: ISOローカル日付
DateTimeFormatter.ISO_OFFSET_TIME
: オフセット付きの時間
ロケール固有のフォーマッタを使う場合はファクトリメソッドを利用する。
ofLocalizedDate
メソッド : ISO暦に対するロケール固有の日付フォーマット
ofLocalizedDateTime
メソッド : ISO暦に対するロケール固有の日時フォーマット
フォーマッタを利用してフォーマットする場合は format
メソッドを呼び出す。
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
formatter.format(LocalDateTime.now());
独自のパターンを指定して DateTimeFormatter インスタンスを取得する場合は static な ofPattern
メソッドを利用する。
java.time
パッケージの Instant クラスは時系列上の単一時点 (起点からの時間量) を表す。Instant インスタンスを取得する場合はファクトリメソッド now
を利用する。
Instant.EPOCH;
Instant.now().getEpochSecond();
Instant.now().toEpochMilli();
Instant.EPOCH
の文字列表現では末尾に Z
が付いており、タイムゾーンの情報が含まれていることを表す。
java.util.Date
クラスや java.util.Calendar
クラスが提供しているに toInstant
メソッドで、従来のオブジェクトと Date and Time API のオブジェクトを相互変換できる。
LocalDateTime.ofInstant(date.toInstant, ZoneId.systemDefault());
Date.from(localDateTime.atZone(ZoneId.systemDefault())).toInstant();
起点からの秒やミリ秒を指定して Instant インスタンスを取得する場合は ofEpochSecond(long)
メソッドや ofEpochMilli(long)
メソッドを利用する。
ZonedDateTime クラスは、java.time.chrono.ChronoZonedDateTime
インタフェースの default メソッドである toInstant
メソッドを継承している。
LocalDateTime クラスは、ChronoLocalDateTime インタフェースの default メソッドとして toInstant
メソッドを提供している。ただし、LocalDateTime は時差情報を持たないため、引数に ZoneOffset オブジェクトを指定する必要がある。
現在の協定世界時 (UTC) を取得する場合は、java.time.ZonedDateTime
クラスの now
メソッドを使う。引数には ZoneId オブジェクトを指定する。ZoneId クラスのサブクラスである ZoneOffset クラスで定数 ZoneOffset.UTC
が定義されている。
java.util.Date
オブジェクトを取得する場合は、ZonedDateTime オブジェクトの toInstant
メソッドでエポックミリ秒を取得し、Date クラスの from
メソッドを使用する。
ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC);
Date date = Date.from(utc.toInstant());
java.io.File
クラスではシンボリックリンクなどの UNIX 系のファイルシステム固有の機能は利用することができない。ファイルサイズや最終更新日時は取得できるが、オーナーやセキュリティなど詳細なファイル属性を扱うことはできない。
入出力ストリームはデバイスとデータの流れを抽象化したもの。
バイナリファイルを扱う場合は InputStream/OutputStream を使う。テキストファイルを扱う場合は Reader/Writer を使う。厳密にはテキストファイルもバイナリなので、InputStream/OutputStream でテキストファイルを扱うことはできる。逆にReader/Writer でバイナリファイルを扱うことはできない。
java.io
パッケージの入出力ストリームでは Decorator パターンが使われている。基本機能を提供する Decorated と、より高度な機能を提供する Decorator で構成される。Decorated は抽象クラスとして提供される。Decorator は Decorated を継承すると同時に Decorated の参照を保持する。
以下の場合、Decorator である BufferedReader/BufferedWriter が、Decorated である FileReader/FileWriter の参照を保持している。
BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"));
java.io.BufferedReader
クラスの read
メソッドは int 値を返す。readLine
メソッドは1行のテキストを読み込んで String 型で返す。ただし、終端の改行コードは含まない。
java.io.BufferedWriter
クラスの write
メソッドは改行コードを書き込まない。改行コードを出力する場合は newLine
メソッドを使用する。
InputStream クラスや Reader クラスではデータの読み取り位置を制御するマーク機能が提供されている。
void mark(int)
: 引数にマーク後に読み取れる文字数を指定して現在の読み取り位置をマークする
void reset
: マークされている読み取り位置に移動する (マークしていない場合は例外がスローされる)
long skip(long)
: 引数で指定された文字数だけ読み取り位置をスキップする
Reader クラスの ready
メソッドは入力ストリームを読み取ることができる場合に true を返す。try-with-resources で close された入力ストリームに対して ready
を呼び出すと例外がスローされる。
java.io.PrintStream
クラスおよび java.io.PrintWriter
クラスでは各プリミティブ型の値を出力する機能を提供する。プラットフォーム固有の改行文字を出力する。
標準入力からデータを読み取るには java.io.Console
クラスを使う。readLine
メソッドで文字列を読み取る。パスワードを読み取る場合は readPassword
メソッドを使う。
オブジェクトをファイルに保存したりネットワーク越しにオブジェクトを転送する場合、Serialize (直列化) と Deserialize (非直列化/復元) を使う。
直列化や復元は JVM によって実行される。対象オブジェクトの型であるクラスに java.io.Serializable
インタフェースを実装する。Serializable インタフェースを実装していないオブジェクトは直列化できない。java.io.Serializable
インタフェースはマーカーインタフェースと呼ばれる。
直列化には java.io.ObjectOutputStream
クラスの writeObject
メソッドを使う。復元には java.io.ObjectInputStream
クラスの readObject
メソッドを使う。
オブジェクトを直列化する際、static 変数と transient キーワード付きで宣言された変数の値は除外される。除外された変数を復元した場合はデフォルト値で初期化される。transient キーワードは、オブジェクトがメモリ上に存在する間だけ有効であることを明示するキーワードで、オブジェクトの直列化はしないがフィールドとしては使いたい変数がある場合に使用する。
java.nio.file.Path
インタフェースは、java.io.File
クラス同様にファイルやディレクトリのパスを表す。クラスではなくインタフェースとして提供されているため、プラットフォーム固有の仕組みを透過的に扱える。
java.nio.file.FileSystem
クラスはプラットフォーム固有のファイルシステムを表す抽象クラス。ファクトリメソッドである getDefault
メソッドで FileSystem の具象クラスを取得する。
Path オブジェクトはファイルシステムに関連付けられているため、FileSystem オブジェクトの getPath
メソッドで取得する。または、java.nio.file.Paths
クラスの get
メソッドで取得する。
Path インタフェースで宣言されているパス操作関連のメソッドは以下の通り。
subpath(int, int)
メソッド : パスの一部を抜き出す (endIndex の要素は結果に含まれない)
resolve
メソッド : 自身のパスと引数の相対パスを結合したパスを返す (引数が絶対パスの場合は引数のパス、引数が空の場合は自身のパスを返す)
resolveSibling
メソッド : 自身の親パスと引数の相対パスを結合したパスを返す (引数が絶対パスの場合、または呼び出し側の Path オブジェクトが相対パスの場合は引数のパスを返す)
normalize
メソッド : 現在のパスに含まれる冗長なパス表現を正規化したパスを返す (カレントディレクトや親ディレクトリを表すピリオドを解決する)
relativize
メソッド : 引数のパスを現在のパスからの相対パスとして解決したパスを返す (解決できない場合は例外をスロー)
java.nio.file.Files
クラスはファイルやディレクトリの操作系の機能を提供するユーティリティクラス。
copy
メソッドでは、コピー先に同名のファイルが存在する場合、java.nio.file.FileAlreadyExistsException
がスローされる。ファイルを上書きする場合は StandardCopyOption.REPLACE_EXISTING
オプションを指定する。
デフォルトでは、ファイル属性はコピーされない。ファイル属性をコピーする場合は StandardCopyOption.COPY_ATTRIBUTES
オプションを指定する。
copy
メソッドでディレクトリをコピーする場合、ディレクトリ内のファイルはコピーされない。
シンボリックリンクをコピーした場合、リンク自体はコピーされない。シンボリックリンクをコピーする場合は LinkOption.NOFOLLOW_LINKS
オプションか StandardCopyOption.REPLACE_EXISTING
オプションを指定する。
java.nio.file.attribute
パッケージにはファイル属性を扱うインタフェースやクラスが含まれる。
BasicFileAttributes インタフェースは一般的なファイルシステムでサポートされる基本的なファイル属性を表す。サブインタフェースである DosFileAttributes インタフェースは DOS ファイルシステムにおけるファイル属性、PosixFileAttributes インタフェースは UNIX/Linux などの POSIX ファイルシステムにおけるファイル属性を扱う。
ファイル属性のセットを属性ビュー (Attribute View) と呼ぶ。属性ビューは AttributeView インタフェースとそのサブクラスで表される。一般的に使われるものとして、BasicFileAttributeView インタフェースと、サブインタフェースである DosFileAttributeView インタフェースと PosixFileAttributeView インタフェースが存在する。
readAttributes
メソッドでファイル属性を表すオブジェクトを取得する。
FileTime time = Files.readAttributes(path, BasicFileAttributes.class).lastAccessTime();
Files クラスの getAttribute
メソッドでファイルやディレクトリのファイル属性を取得できる。取得する属性名には属性ビュー名 (basic/dos/posix) を指定する。省略した場合は basic となる。getAttribute
メソッドの戻り値は Object 型なので適切な型にキャストする。
Long size = (Long) Files.getAttributes(path, "size");
FileTime time = (FileTime) Files.getAttributes(path, "creationTime");
int uid = (Integer) Files.getAttribute(path, "unix:uid");
ディレクトリ階層を再帰的にトラバースするには、java.nio.file.Files
クラスの walkFileTree
メソッドを使用する。walkFileTree
メソッドに指定する FileVisitor インタフェースの実装クラスで再帰処理を実装する。コールバックされたメソッドでは java.nio.file.FileVisitResult
列挙定数を返す。
Files クラスが提供する Stream オブジェクトを返すメソッドは以下の通り。
lines(Path)
メソッド : テキストファイルの内容を Stream<String>
オブジェクトとして返す
list(Path)
メソッド : 引数に指定した Path オブジェクトのディレクトリ内のエントリ (ファイルおよびサブディレクトリ) を Stream<Path>
オブジェクトとして返す (ファイルを表す Path オブジェクトを指定すると java.nio.file.NotDirectoryException
がスローされる)
walk
メソッド : 深さ優先でファイルツリーを再帰的にトラバースし、結果を Stream<Path>
オブジェクトとして返す (デフォルトではシンボリックリンクは辿らない)
Files クラスの readAllLines
メソッドは Path オブジェクトを引数に取ってファイル内のすべての行を文字列として格納した List<String>
オブジェクトを返す。
java.io.File
クラスの list
メソッドは自身のディレクトリの直下のファイルおよびディレクトリを String 配列で返す。listFiles
メソッドは自身のディレクトリの直下のファイルおよびディレクトリを File 配列で返す。
並行性
並行処理ユーティリティは、java.util.concurrent
パッケージとサブパッケージである java.util.concurrent.atomic
パッケージおよび java.util.concurrent.locks
パッケージから構成される。スレッドプール、並行コレクション、アトミック変数、カウンティングセマフォなどの機能を提供する。
スレッドプールは、あらかじめスレッドを生成して貯蔵することでスレッド生成のオーバーヘッドを軽減し、スレッドの管理性を向上させる。Executor フレームワークがスレッドプールの機能を提供する。
並行コレクションは、複数スレッドから並行アクセスされることを前提に設計されている。従来の同期化コレクションでは、状態へのアクセスを直列化することでスレッドセーフを実現している。
アトミック変数は、一連の処理が完全に終了しているか未着手であるかを保証することで、データの妥当性や整合性を保証する。アトミック (原子性) とは、2つ以上の処理によって構成される一連の処理においてそれぞれの処理が不可分であることを表す概念。java.util.concurrent.atomic
パッケージのクラスによって提供される。
カウンティングセマフォは、リソースに対して並行アクセス可能なスレッド数を任意に設定できる仕組み。java.util.concurrent.Semaphore
クラスによって提供される。セマフォは、リソースに対して並行アクセスするプロセスやスレッド間における同期や割り込みを制御する仕組み。カウンティングセマフォの他に、リソースに対するアクセスが可能か不可能かいずれかの値を取るバイナリセマフォがある。
コレクションから Iterator オブジェクトを取得した後に元のコレクションの要素を操作した場合、ConcurrentModificationException がスローされる。並行処理ユーティリティに含まれるコレクションを使用した場合は例外はスローされない。
java.util.concurrent.ConcurrentMap
インタフェースは従来の Map 実装クラスに加えてアトミック操作をサポートする。ロックストライピング (細粒度のロック方式) によって並行処理における実行性能を最適化している。パフォーマンス向上のトレードオフとして size
メソッドや isEmpty
メソッドの厳密性要件は弱いため、正確な値を返すとは限らない。設計方針は Weakly Consistent (弱い整合性) である。従来の Hashtable クラスや Collections.synchronizedMap
メソッドはマップ全体をロックして排他的アクセスを実現する。
java.util.concurrent.CopyOnWriteArrayList
クラスは、リストの要素を変更するメソッドを呼び出すたびに内部の配列のコピーを作成して、コピー作成時に操作を実行する。リストのサイズが大きくなるとコピー処理のオーバーヘッドが大きくなりパフォーマンスが悪くなるため、大量データのリストに対する更新処理には List の同期化ラッパーを返す Collections.synchronizedList
メソッドを使う。
AtomicInteger クラスの accumulateAndGet(int, IntBinaryOperator)
メソッドや getAndAccumulate(int, IntBinaryOperator)
メソッドは値の更新値をカスタマイズすることができる。引数に指定する IntBinaryOperator の getAsInt
メソッドは、第1引数に現在値、第2引数に更新値を受け取る。単純に更新値分を増減する場合は、getAndAdd(int)
メソッドや addAndGet(int)
メソッドを使う。
java.util.concurrent.CyclicBarrier
クラスは、スレッドパーティ内のスレッドの動作を協調して動作させる機能を提供する。スレッドはバリアに到達すると他のスレッドが到達するまで待機する。他のスレッドがバリアに到達するとバリアが解除される。この動作をトリップと呼ぶ。トリップ可能とするスレッドパーティ数はコンストラクタで指定する。トリップ時に実行する処理を Runnable オブジェクトで指定することができる。
Thread クラスではタスクごとにスレッドを生成して複数のタスクを非同期に実行できる。スレッド数の上限は設定できない。上限を超えてスレッドを生成すると java.lang.OutOfMemoryError
が発生する。スレッドの生成と破棄にはオーバーヘッドが生じる。
Executor フレームワークは主にスレッドプールの実装を提供する。その他、タスク実行におけるライフサイクル管理や監視、統計収集などの機能を提供する。
ScheduledThreadPoolExecutor クラスはタスクのスケジューリング (遅延実行や周期実行) を実現する。
java.util.concurrent.Executor
インタフェースはタスクの実行者と実行方法 (スレッドの生成と実行) を分離する。
Executor インタフェースのサブインタフェースである ExecutorService インタフェースはタスクの終了管理と進行状況を管理する機能を定義する。ExecutorService インタフェースのサブインタフェースである ScheduledExecutorService インタフェースはタスクのスケジュール管理の機能を定義する。
java.util.concurrent.Executor
インタフェースは Runnable オブジェクトを引数に取って戻り値を返さない execute
メソッドを定義している。Executor の実装を取得する場合は java.util.concurrent.Executors
のファクトリメソッドを利用する。execute
メソッドではなく、ExecutorService の submit
メソッドを使用することも可。
Executor フレームワークの実装は以下の通り。
public class MyExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Thread(command).run();
}
}
タスクを実行する場合は以下の通り。
Runnable task = () -> System.out.println("OK");
Executor executor = new MyExecutor();
executor.execute(task);
Runnable インタフェースの run
メソッドでタスクを実行する場合、戻り値を返したりチェック例外をスローすることはできない。
戻り値を返したりチェック例外をスローする場合は、java.util.concurrent.Callable<T>
インタフェースを使用する。Callable インタフェースの call
メソッドでタスクを実行するときは ExecutorService の submit
メソッドを使用する。(submit
メソッドの内部で call
メソッドを呼び出す)
submit
メソッドは非同期タスクの実行結果を表す Future オブジェクトを返す。Future オブジェクトの get
メソッドで実行結果を取得する。
Future<T>
インタフェースは非同期で実行したタスクの実行結果を取得できる。その他、タスク完了の判定やキャンセルなどの機能を提供する。Runnable インタフェースで実装したタスクを実行した場合は Future オブジェクトの get
メソッドは null を返す。
Callable タスクの実装は以下の通り。
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "OK";
}
}
Callable タスクを実行して Future オブジェクトから結果を取得する場合は以下の通り。
ExecutorService service = Executors.newSingleThreadExecutor();
Future<String> future = service.submit(new MyCallable());
System.out.println(future.get());
ExecutorService インタフェースは Executor の終了に関する機能を提供する。
shutdown
メソッドはタスクキューに残っているタスクを実行してから Executor を終了する。shutdownNow
メソッドはタスクキューに残っているタスクをキャンセルして Executor を終了する。
終了した Executor は新しいタスクを受け付けず、タスクの実行を依頼しようとすると java.util.concurrent.RejectedExecutionException
をスローする。
isShutdown
メソッドは Executor が終了しているかどうかを返す。実行中のタスク有無は問わない。Executor のタスクキュー内のタスクがすべて実行されたかどうかは isTerminated
メソッドを使う。
Fork/Join フレームワークは、大きいタスクを小さいタスクに分割し、分割したタスクを複数のスレッドで同時並行に実行する仕組みを提供する。マルチコアCPUを効率的に利用することを目的とする。
Work-stealing と呼ばれるアルゴリズムを採用している。ワーカースレッドは自身のワークキューに両端キューを持つ。分割 (fork) したタスクをワークキューに挿入する。ワークキュー内のタスクをすべて実行したワーカースレッドは、他のスレッドのワークキューの末尾から未処理のタスクを横取り (stealing) して処理する。ワークキューの先頭にはキューを所有するワーカースレッドのみがアクセスできる。
Fork/Join フレームワークを利用してタスクを実行するには、ExecutorService インタフェースの実装クラスである java.util.concurrent.ForkJoinPool
クラスを使う。実行するタスクは、抽象クラスの java.util.concurrent.ForkJoinTask<V>
クラスを使う。
ForkJoinPool
クラスでタスクを実行するメソッドは以下の通り。
void execute(ForkJoinTask<?>)
メソッド : 非同期でタスクを実行して処理結果は返さない
<T> T invoke(ForkJoinTask<T>)
メソッド : タスクの完了を待機して処理結果を返す
<T> ForkJoinTask<T> submit(ForkJoinTask<T>)
メソッド : 非同期でタスクを実行して処理結果を返す (ForkJoinTask オブジェクトを返す)
通常、ForkJoinPool で実行するタスクには java.util.concurrent.ForkJoinTask<V>
クラスのサブクラスを利用する。いずれも抽象クラスで、抽象メソッドとして compute
メソッドを宣言している。継承するサブクラスでは compute
メソッドを実装する。
RecursiveAction
クラス : 処理結果として戻り値が不要な場合
RecursiveTask<V>
クラス : 処理結果として戻り値が必要な場合 (戻り値の型は V)
compute
メソッドは、Executor となる ForkJoinPool オブジェクトの execute
メソッド、invoke
メソッド、submit
メソッドのいずれかのメソッドから間接的に呼ばれる。
ForkJoinTask クラスの fork
メソッドは現在のスレッドプール内でタスクを非同期に実行する。join
メソッドは実行が完了したタスクの結果を返す。
以下、RecursiveTask クラスの実装例。最初にタスクを fork
メソッドで非同期実行しつつ、次のタスクを compute
メソッドで実行する。最後に最初のタスクを join
メソッドで待ち合わせて結果を返す。実際は、compute
メソッドの中でさらに並列実行されている可能性はある。
MyTask task1 = new MyTask(...);
MyTask task2 = new MyTask(...);
task1.fork();
task2.compute() + task1.join();
ストリームAPIの処理は、順次処理か並列処理のいずれかで実行する。stream
メソッドでストリームオブジェクトを生成した場合は順次実行モードになる。parallelStream
メソッドでストリームオブジェクトを生成した場合は並列実行モードになる。
順次処理から並列処理の変更は parallel
メソッドを使う。並列処理から順次処理の変更は sequential
メソッドを使う。
ストリームオブジェクトの要素数や処理内容によっては並列処理でもパフォーマンスが劣化することがある。
JDBCによるデータベース・アプリケーションの作成
JDBC は JDBC API と JDBC ドライバの2つのレイヤから構成される。JDBC ドライバは JDBC API で規定されるインタフェースの実装を提供する。具体的には、java.sql
パッケージの Connection インタフェースと Statement インタフェースと ResultSet インタフェース。
DriverManager と SQLException はインタフェースではなくクラス。
JDBC は、接続URLと呼ばれる識別子を使用してデータベースに接続する。ポート番号を省略した場合はDB製品のデフォルトが使われる。
jdbc:<db_product_name>://<hostname>:<port>/<db_name>?<option...>
DBに接続するには、java.sql.DriverManager
クラスの static メソッドである getConnection
メソッドを利用する。getConnection
メソッドは java.sql.Connection
オブジェクトを返す。DBの接続に失敗した場合は java.sql.SQLException
がスローされる。
JDBC 3.0 では Class.forName("JDBC ドライバのクラス名")
で JDBC ドライバをロードする必要がある。JDBC 4.0 ではクラスパス上の JDBC ドライバを自動でロードするため Class.forName
メソッドの呼び出しは不要。
JDBC 4.0 以降に準拠したドライバでは、ドライバの完全修飾クラス名を記述した java.sql.Driver
ファイルを jar ファイルの META-INF/services
ディレクトリに配置する。
トランザクションの分離レベルに関する設定は、java.sql.Connection
インタフェースの setTransactionIsolation
メソッドを使用する。DBそのものの設定は Connection オブジェクトの getMetaData
メソッドで返される DatabaseMetaData オブジェクトから取得する。
DBにSQL文を発行するには java.sql.Statement
オブジェクトを使用する。Statement オブジェクトの取得には、Connection オブジェクトの createStatement
メソッドを使用する。一般的には Statement インタフェースのサブインタフェースとして、 プリコンパイルされたSQLを実行するための PreparedStatement やストアドプログラムを実行するための CallableStatement を使用する。
SQLを実行する場合は Statement インタフェースで宣言されている以下のメソッドを利用する。
executeQuery
メソッド : クエリ (SELECT) を実行して ResultSet オブジェクトを返す
executeUpdate
メソッド : 更新系のSQLやDDL文 (CREATE TABLE など) を実行して更新された行数を int で返す
execute
メソッド : クエリ/更新系SQLを実行する (実行したSQLがクエリなら true、実行したSQLがクエリ以外の場合は false を返す)
execute
メソッドの戻り値が true の場合、ResultSet オブジェクトが返っているため、getResultSet
メソッドで ResultSet オブジェクトを取得する。execute
メソッドの戻り値が false の場合、getUpdateCount
メソッドで更新行数を取得することができる。
Java 7 以降なら try-with-resources を使って close する。
try (Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM foo")) {
while (rs.next()) {
}
} catch (SQLException e) {
e.printStackTrace();
}
ResultSet オブジェクトは、クエリの結果セットを表すオブジェクト。内部のポインタ (カーソル) で処理対象の行 (レコード) を指定する。レコードの特定列のデータを取得するには ResultSet インタフェースが宣言しているデータ型に対応した getXxx
メソッドを利用する。
getXxx
メソッドで列インデックスをを指定する場合、最初の列のインデックスは1であることに注意。
カーソルの初期位置は先頭レコードの1つ前を指す。カーソルが先頭レコードの1つ前を指している状態でレコードを操作するメソッドを呼び出すと SQLException がスローされる。同様にカーソルが最終レコードの1つ後ろを指している状態でレコードを操作するメソッドを呼び出すと SQLException がスローされる。
一般的に、カーソルを移動する際は next
メソッドを利用する。移動先にレコードが存在する場合は true を返す。レコードが存在しない場合は false を返す。
ResultSet インタフェースから日付や時刻を取得する場合は以下のメソッドを使う。戻り値はいずれも java.util.Date
のサブクラス。
getDate
メソッド : java.sql.Date
オブジェクトを返す (日付)
getTime
メソッド : java.sql.Time
オブジェクトを返す (時間)
getTimestamp
メソッド : java.sql.Timestamp
オブジェクトを返す (日付と時間)
java.sql.Date
クラス、java.sql.Time
クラス、java.sql.Timestamp
クラスでは、それぞれ toLocalDate
メソッド、toLocalTime
メソッド、toLocalDateTime
メソッドが提供されている。(Date and Time API が提供する型に変換する)
ResultSet オブジェクトは、元の Statement オブジェクトや Connection オブジェクトが close された場合は利用できない。1つの Statement オブジェクトから取得できる有効な ResultSet オブジェクトは1つのみ。2つ以上の ResultSet オブジェクトを取得すると1つ目の ResultSet オブジェクトは close される。close された ResultSet オブジェクトを利用すると SQLException がスローされる。
スクロール可能な ResultSet (Scrollable ResultSet) はカーソルを任意の位置に移動できる。
createStatement
メソッドは、引数を取らないか、2つの引数を取るメソッドのみ提供されている。
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
第1引数には ResultSet オブジェクトの型を示す定数を指定する。
- ResultSet.TYPE_FORWARD_ONLY : 順方向のみ (デフォルト)
- ResultSet.TYPE_SCROLL_INSENSITIVE : スクロール可能 (元のデータの変更は ResultSet に反映されない)
- ResultSet.TYPE_SCROLL_SENSITIVE : スクロール可能 (元のデータの変更が ResultSet に反映される)
第2引数には ResultSet によるレコードの更新可否を表す定数を指定する。
- ResultSet.CONCUR_READ_ONLY : 更新できない ResultSet オブジェクトを示す
- ResultSet.CONCUR_UPDATABLE : 更新できる ResultSet オブジェクトを示す
スクロール可能な ResultSet が提供しているカーソルを相対位置に移動するメソッド。
boolean next
メソッド : カーソルを現在位置から順方向に1行移動する
boolean previous
メソッド : カーソルを前の行に移動する
boolean relative(int)
メソッド : カーソルを正または負の相対行数だけ移動する
スクロール可能な ResultSet が提供しているカーソルを絶対位置に移動するメソッド。
boolean absolute(int)
メソッド : カーソルを指定された行番号に移動する
boolean afterLast
メソッド : カーソルを終端に移動する (最終行の次の行)
boolean beforeFirst
メソッド : カーソルを先端に移動する (先頭行の前の行)
boolean first
メソッド : カーソルを先頭行に移動する
boolean last
メソッド : カーソルを最終行に移動する
absolute
メソッドで先頭行に移動する場合は1を指定する。0を指定した場合は先頭行の前の空行を指す。レコード数より大きな行番号を指定した場合は最終行の次の空行を指す。
更新可能な ResultSet では、レコードの挿入、更新、削除ができる。変更内容をDBに反映するには updateRow
メソッドを呼び出す。updateRow
メソッドを呼び出すまでは元のデータを返す。また、updateRow
メソッドを呼び出す前に cancelRowUpdates
メソッドを呼び出すと、ResultSet への変更を取り消すことができる。
更新可能なResultSet で新規レコードを挿入するには、moveToInsertRow
メソッドを呼び出してカーソルを挿入専用行に移動する必要がある。データをセットする場合は、データ型に対応した updateXxx
メソッドを利用し、insertRow
メソッドでレコードをテーブルに挿入する。
カーソルを挿入専用行に移動する直前の位置に戻すには moveToCurrentRow
メソッドを呼び出す。
java.sql.SQLException
クラスの getErrorCode
メソッドはベンダー固有のエラーコードを返す。getSQLState
メソッドはSQLステートを返す。SQLステートは ANSI SQL で標準化されているデータベース製品共通の情報。
SQLException は setNextException(SQLException)
メソッドでチェーンすることができる。チェーンした例外は SQLException getNextException
メソッドや Iterator<Throwable> iterator
メソッドで取得できる。
特定の地域にアプリケーションを対応させることをローカライズと呼ぶ。言語や数値、日付、通貨などを特定の国や地域で使用されている形式で表示する。ロケールは java.util.Locale
クラスがサポートする。
Locale locale = new Locale("言語コード", "国コード", "バリアント値");
- 言語コード : 2桁の英小文字で表される (日本語なら ja)
- 国コード : 2桁の英大文字で表される (日本なら JP)
- バリアント値 : Locale のバリエーションを示すために使用される任意の値
システムのデフォルトの Locale オブジェクトを取得するには Locale クラスの static メソッドで宣言されている getDefault
メソッドを使用する。
Locale コンストラクタで指定する言語コードや国コードは正しい書式としての妥当性はチェックされない。Locale クラスの static メンバークラスである Locale.Builder クラスのセッターメソッドでロケールに関連する情報を設定する場合は書式がチェックされる。書式が不正な場合は java.util.IllformedLocaleException
がスローされる。
Locale locale = new Locale.Builder().setLanguage("cat").setRegion("ES").build();
一般的によく使われる国や言語のロケールに対応した Locale オブジェクトを参照する public な定数が提供されている。
Locale locale = Locale.JAPAN;
プロパティは key=value
形式で表示されるデータ。プロパティを記述したファイルをプロパティファイルと呼ぶ。プロパティファイルは、java.util.Properties
クラスで扱う。
Properties クラスは、特定のフォーマットに従うテキストファイルか XML ファイルに対応する。テキストファイルの場合、キーと値は =
か :
で区切って1行ごとに記述する。#
や !
で始まる行はコメントとして扱う。
プロパティファイルは FileInputStream クラスか FileReader クラスで読み込む。
Properties prop = new Properties();
prop.load(input);
String value = prop.getProperty("key");
getProperty
メソッドは、指定されたキーが存在しない場合は null を返す。第2引数にデフォルト値を指定するオーバーロードメソッドが存在する。
Properties クラスは、Map インタフェースの実装である java.util.Hashtable
クラスのサブクラスである。Map インタフェースの default メソッドである forEach
メソッドで読み込んだプロパティを繰り返し処理できる。単純にコンソールに出力する場合は、PrintStream オブジェクトか PrintWriter オブジェクトを引数に取る list
メソッドを利用する。
リソースバンドル (Resource Bundle) は、主にUIにおける言語や表示形式をロケールごとに1つにまとめたもの。
リソースバンドルは、ロケールごとに用意されたクラスファイルかプロパティファイル (.properties) で表す。標準では XML 形式には対応していない。
リソースバンドルは、<基底名_言語コード_国コード>
の形式でファイル名を付ける。リソースバンドルはクラスパス上に配置する。パッケージディレクトリに配置した場合は Java のクラスと同じようにパッケージ名で修飾する。
リソースバンドルは java.util.ResourceBundle
クラスで扱う。ResourceBundle クラスは抽象クラスであるため、static メソッドである getBundle
メソッドを使用して ResourceBundle オブジェクトを取得する。
getBundle
メソッドでリソースバンドルを検索する流れは以下の通り。リソースバンドルが見つからない場合は java.util.MissingResourceException
がスローされる。
<言語コード_国コード>
が一致するリソースバンドル (クラスファイル→プロパティファイル)
<言語コード>
が一致するリソースバンドル (クラスファイル→プロパティファイル)
<基底名>
が一致するリソースバンドル (クラスファイル→プロパティファイル)
クラスを使用してリソースバンドルを作成する場合は ResourceBundle クラスのサブクラスである java.util.ListResourceBundle
クラスや java.util.PropertyResourceBundle
クラスを継承したクラスを作成する。ListResourceBundle クラスは抽象クラスであり、サブクラスで抽象メソッドである getContents
メソッドをオーバーライドする。getContents
メソッドはキーと値のペアを Object[][]
型で返すように実装する。
protected Object[][] getContents() {
return new Object[][] {
{ "key1", "value1" },
{ "key2", "value2" },
};
}
値を取得する場合は ResourceBundle クラスの getString
メソッドを呼び出す。