徹底攻略Java SE 11 Gold問題集[1Z0-816]対応で勉強した際に調べた結果を共有いたします。
1章 問題 1
解説で「static インナー クラス」という用語が使われていますが、これは厳密には不正確な表現です。Java 言語仕様には以下のように記述されています。
An inner class is a nested class that is not explicitly or implicitly declared static.
つまり、static なネステッド クラスのことを「インナー クラス」とは呼ばないのです。
本件は『プロになる Java』と説明が違っていたために気がつきました。『プロになる Java』「14.1.6 ネステッドクラスとインナークラス」では以下のように説明されていました。
クラスの中で定義するクラスをネステッドクラスといいます。
static が付いていないクラスはインスタンスクラスとは呼ばずにインナークラスといいます。static クラスは正式な用語ではありませんが、static なネステッドクラスを表す場合に使われます。
言語仕様と照らし合わせると、『プロになる Java』の説明のほうが正確な記述だと言えます。
1 章 問題 5
Java 15 までは、static でないネステッド クラスに static フィールドは持てず、コンパイル エラーになります。
class Outer {
class Inner {
static int field;
}
}
詳しいことは知らないのですが、レコード クラスで問題があったため Java 16 で許容されるようになっていました。
1 章 問題 11
Java 言語仕様には以下の記述があります。
- If the form is TypeName . super . [TypeArguments] Identifier, then:
(中略)
Otherwise, TypeName denotes the interface to be searched, I.
Let T be the type declaration immediately enclosing the method invocation. It is a compile-time error if I is not a direct superinterface of T, or if there exists some other direct superclass or direct superinterface of T, J, such that J is a subtype of I.
問題集の interface A は直接のスーパーインターフェースになっていて、他の直接のスーパーインターフェースは A のサブタイプではないので、この形式でデフォルト メソッドを呼び出すことができます。
コンパイル エラーになるのは次の2つの場合です。
I が T の直接的なスーパーインターフェースでない
以下のような場合です。JEP 330: Launch Single-File Source-Code Programs の java コマンドをインタープリターのように使う方法で試しやすいように、main クラスを一番上に書いています。
public class T implements J {
public static void main(String[] args) {
new T().methodAtT();
}
void methodAtT() {
I.super.method();
}
}
interface J extends I {}
interface I {
default void method() {
System.out.println("I");
}
}
I 以外に T の直接的なスーパークラスか直接的なスーパーインターフェース J があり、それが I のサブタイプである。
これは以下のような場合です。
public class T implements I, J {
public static void main(String[] args) {
new T().methodAtT();
}
void methodAtT() {
I.super.method();
}
}
interface J extends I {}
interface I {
default void method() {
System.out.println("I");
}
}
13 章 問題 8
この挙動が仕様として納得できなかったので検索してみたところ、StackOverflow に同様の質問がありました。
これはバグで、現在も open になっています。
13 章 問題 33
私は並列処理が苦手なのですが、設問のプログラムは hello メソッド内でもう一方の Sample インスタンスでロックしようとしていないので、デッドロックは起きないように思います。設問の main メソッドはスレッドの終了を待たずに正常終了してしまうので、ExecutorService を使って、shutdown()
の後に awaitTermination(1L, TimeUnit.Days)
としてみたのですが、手元ではデッドロックは発生しませんでした。
可能性としては、次のようにしてプログラムが止まることはあると思いますが、これはロックの取得待ちで止まっているわけではないので、デッドロックではないはずです。
- スレッド1が s1.hello() を実行し、s1.test が非 null になる
- スレッド2がスレッド1が書き換えた s1.test を参照し、非 null なので、while ループを抜けられない
- スレッド1が s1.bye() を実行し、スレッド1では s1.test が null に戻る
- スレッド2が、s1.test が volatile でないのでスレッド1が更新した値を読むことができず、非 null の値を読み続けて while ループを抜けられない
最後に
合格には上記のような知識は必要ありませんが、どなたかのお役に立てれば幸いです。