Java Silver クラスの定義とインスタンスの使用 問題

ゆめさくが作った練習問題です。本試験の問題ではありません。

クラスの定義とインスタンスの使用の問題を2⁠2問、小分野ごとにまとめました。選ぶと、その場で正誤と解説が出ます。

ログインなしで解けます。答えはこのブラウザの中だけに残り、どこにも送りません。

この分野の小分野

  • クラス、メソッド、コンストラクタ、オーバーロード(11問)
  • static、アクセス修飾子、パターン・マッチング、レコード(11問)

クラスの定義とインスタンスの使用の問題

22 問

選ぶと、すぐに正誤と解説が出ます。選んだ答えは変えられません。

  1. クラス、メソッド、コンストラクタ、オーバーロード

    問 1次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Point {
        int x;
    }
    
    public class Main {
        public static void main(String[] args) {
            Point p = new Point();
            Point q = p;
            q.x = 5;
            p = new Point();
            System.out.println(p.x + " " + q.x);
        }
    }
  2. 問 2次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        double rate;
        boolean done;
        Integer count;
    
        public static void main(String[] args) {
            Main m = new Main();
            System.out.println(m.rate + " " + m.done + " " + m.count);
        }
    }
  3. 問 3次のコードの (X) の時点で、ガベージ・コレクションの対象になれる StringBuilder のオブジェクトはどれですか。

    public class Main {
        public static void main(String[] args) {
            StringBuilder a = new StringBuilder("A");
            StringBuilder b = new StringBuilder("B");
            StringBuilder c = new StringBuilder("C");
            a = b;
            c = a;
            b = null;
            // (X)
            System.out.println(a + " " + c);
        }
    }
  4. 問 4次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static String grade(int score) {
            if (score >= 80) {
                return "A";
            } else if (score >= 50) {
                return "B";
            }
        } // (A)
    
        public static void main(String[] args) {
            System.out.println(grade(70));
        }
    }
  5. 問 5次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static void edit(StringBuilder sb, String s) {
            sb.append("!");
            s = s + "!";
            sb = new StringBuilder("new");
            sb.append("?");
        }
    
        public static void main(String[] args) {
            StringBuilder sb = new StringBuilder("hi");
            String s = "hi";
            edit(sb, s);
            System.out.println(sb + " " + s);
        }
    }
  6. 問 6次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static int a(int... n) { return n.length; }               // (A)
        static int b(String s, int... n) { return n.length; }     // (B)
        static int c(int... n, String s) { return n.length; }     // (C)
    
        public static void main(String[] args) {
            System.out.println("" + a() + b("x", 7));
        }
    }
  7. 問 7次の 2 つのクラスはどちらもコンストラクタを書いていません。コンパイラが自動で足す既定のコンストラクタのアクセス修飾子の組み合わせとして正しいものはどれですか。Report と Memo は同じパッケージの別のファイルにあります。

    // Report.java
    public class Report {
    }
    
    // Memo.java
    class Memo {
    }
  8. 問 8次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Timer {
        int sec;
    
        Timer() {
            this(60);
        }
    
        Timer(int sec) {
            this();
            this.sec = sec;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(new Timer().sec);
        }
    }
  9. 問 9次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Gauge {
        int a = 5;
    
        {
            a += 1;
        }
    
        Gauge() {
            a *= 10;
        }
    
        {
            a -= 2;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(new Gauge().a);
        }
    }
  10. 問 10次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static void show(Long x) { System.out.println("Long"); }
        static void show(Object x) { System.out.println("Object"); }
        static void show(int... x) { System.out.println("int..."); }
    
        public static void main(String[] args) {
            show(5);
        }
    }
  11. 問 11次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static void show(int a, long b) { System.out.println("int,long"); }
        static void show(long a, int b) { System.out.println("long,int"); }
    
        public static void main(String[] args) {
            show(1, 2); // (A)
        }
    }
  12. static、アクセス修飾子、パターン・マッチング、レコード

    問 12次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Visit {
        static int total;
    
        Visit() {
            total++;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            new Visit();
            new Visit();
            Visit v = null;
            System.out.println(v.total);
        }
    }
  13. 問 13次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static final int LIMIT;                  // (A)
        static final String NAME = "box";
    
        static {
            LIMIT = NAME.length();               // (B)
        }
    
        public static void main(String[] args) {
            System.out.println(LIMIT + " " + NAME);
        }
    }
  14. 問 14次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Config {
        static int level = 5;
    
        static {
            System.out.print("S");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.print("A");
            Config c = null;
            System.out.print("B");
            System.out.print(Config.level);
        }
    }
  15. 問 15Tool と Main は同じファイルにあり、同じパッケージに属します。次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Tool {
        public int a = 1;
        protected int b = 2;
        int c = 3;
        private int d = 4;
    }
    
    public class Main {
        public static void main(String[] args) {
            Tool t = new Tool();
            int sum = 0;
            sum += t.a; // (A)
            sum += t.b; // (B)
            sum += t.c; // (C)
            sum += t.d; // (D)
            System.out.println(sum);
        }
    }
  16. 問 16次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        static String label(Object o) {
            if (!(o instanceof Integer n)) {
                return "other";
            }
            return "num" + (n + 1);
        }
    
        public static void main(String[] args) {
            System.out.println(label(41) + " " + label("41"));
        }
    }
  17. 問 17次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    record Range(int low, int high) {
        Range {
            if (low > high) {
                int t = low;
                low = high;
                high = t;
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Range r = new Range(9, 3);
            System.out.println(r.low() + "-" + r.high());
        }
    }
  18. 問 18次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    record Book(String title, int pages) { }
    
    public class Main {
        public static void main(String[] args) {
            Book b = new Book("Java", 300);
            System.out.println(b.title() + " / " + b);
        }
    }
  19. 問 19次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    record Point(int x, int y) { }
    
    public class Main {
        public static void main(String[] args) {
            Point a = new Point(1, 2);
            Point b = new Point(1, 2);
            System.out.println((a == b) + " " + a.equals(b) + " " + (a.hashCode() == b.hashCode()));
        }
    }
  20. 問 20次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    record Counter(int value) {
        void reset() {
            value = 0;              // (A)
        }
    
        int next() {
            return value + 1;       // (B)
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Counter c = new Counter(5);
            c.reset();
            System.out.println(c.next());
        }
    }
  21. 問 21次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    record Item(String name, int price) {
        static int created;                                      // (A)
    
        static Item free(String n) { return new Item(n, 0); }    // (B)
    
        int withTax() { return price * 110 / 100; }              // (C)
    
        int stock;                                               // (D)
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(Item.free("pen").withTax());
        }
    }
  22. 問 22次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    record Size(int w, int h) { }
    
    class Square extends Size {         // (A)
        Square(int s) { super(s, s); }  // (B)
    }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(new Square(2).w());
        }
    }

22 問中 0 問に答えました

解説の一覧

解いたあとに読み返せるよう、全問の解説を並べています。問いを開くと解説が出ます。

クラス、メソッド、コンストラクタ、オーバーロード

問1次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Point {
    int x;
}

public class Main {
    public static void main(String[] args) {
        Point p = new Point();
        Point q = p;
        q.x = 5;
        p = new Point();
        System.out.println(p.x + " " + q.x);
    }
}

正解は「0 5」です。new Point() はそのたびに別のオブジェクトを作り、変数にはそのオブジェクトへの参照が入ります。q = p で q は p と同じオブジェクトを指すので、q.x = 5 はそのオブジェクトの x を 5 にします。続く p = new Point() で p だけが新しいオブジェクトを指し直し、その x は int の既定値の 0 です。q は最初のオブジェクトを指したままなので 5 です。よって「0 5」です。「0 0」は q.x = 5 が q の手元の写しにしか効かないと考えた誤り、「5 5」は p を作り直しても前のオブジェクトを指すと考えた誤り、「5 0」は p と q を取り違えています。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠4.html#jls-⁠4.3.1

問2次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    double rate;
    boolean done;
    Integer count;

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.rate + " " + m.done + " " + m.count);
    }
}

正解は「0.0 false null」です。フィールド (インスタンス変数) は、初期値を書かなくても型ごとの既定値で初期化されます。double は 0.0、boolean は false、Integer のような参照型は null です。Integer はラッパー・クラスなので int と同じ 0 にはならず、参照型の既定値の null になります。「0 false 0」は double を 0 と表示し、Integer を int と同じに扱った誤りです。「0.0 true null」は boolean の既定値を取り違えています。「コンパイルエラーになる」は、初期化していないローカル変数を使ったときの決まりとの混同で、フィールドには当てはまりません。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠4.html#jls-⁠4.1⁠2.5

問3次のコードの (X) の時点で、ガベージ・コレクションの対象になれる StringBuilder のオブジェクトはどれですか。
public class Main {
    public static void main(String[] args) {
        StringBuilder a = new StringBuilder("A");
        StringBuilder b = new StringBuilder("B");
        StringBuilder c = new StringBuilder("C");
        a = b;
        c = a;
        b = null;
        // (X)
        System.out.println(a + " " + c);
    }
}

正解は「A と C を持つ 2 つ」です。オブジェクトは、動いているスレッドからどの参照をたどっても届かなくなると、ガベージ・コレクションの対象になれます。順に追うと、a = b で A のオブジェクトを指す変数が無くなります。c = a で c は B を指し、C のオブジェクトを指す変数も無くなります。b = null としても、B は a と c がまだ指しています。よって (X) で届かないのは A と C の 2 つで、出力は「B B」です。「A を持つ 1 つだけ」は c = a で C が手放されることを見落としています。「3 つ」は b = null で B も対象になると考えた誤りで、a と c が残っています。「無い」は参照を付け替えても前のオブジェクトが残ると考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠2.html#jls-⁠1⁠2.6.1

問4次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static String grade(int score) {
        if (score >= 80) {
            return "A";
        } else if (score >= 50) {
            return "B";
        }
    } // (A)

    public static void main(String[] args) {
        System.out.println(grade(70));
    }
}

正解は「(A) の行でコンパイルエラーになる」です。戻り値の型が void でないメソッドは、本体の終わりまで return を通らずに進めてはいけません。grade の if と else if はどちらも条件しだいで、両方とも偽になる道があります。その道では return が無いまま本体の最後に着くので、javac は閉じかっこの (A) の行で missing return statement というエラーを出します。実行するときの値は関係なく、コンパイルの段階で止まります。「B と表示される」は grade(7⁠0) が else if の return を通ることだけを見た誤りです。「null と表示される」は return が無いと null が返ると考えた誤り、「実行時に例外が出る」はコンパイルが通る前提の誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.7

問5次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static void edit(StringBuilder sb, String s) {
        sb.append("!");
        s = s + "!";
        sb = new StringBuilder("new");
        sb.append("?");
    }

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("hi");
        String s = "hi";
        edit(sb, s);
        System.out.println(sb + " " + s);
    }
}

正解は「hi! hi」です。Java の引数は値渡しで、メソッドを呼ぶと引数の値が新しい仮引数に写されます。参照型では参照の値が写されるので、仮引数 sb と呼び出し側の sb は最初は同じ StringBuilder を指します。sb.append("!") はそのオブジェクトを変えるので、呼び出し側も hi! になります。s = s + "!" は新しい String を作って仮引数 s に入れ直すだけで、呼び出し側の s は hi のままです。sb = new StringBuilder("new") も仮引数の付け替えなので、そのあとの append は呼び出し側に届きません。よって「hi! hi」です。「new? hi!」と「hi! hi!」は仮引数への代入が呼び出し側に及ぶと考えた誤り、「hi hi」は append の変更も届かないと考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.1

問6次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static int a(int... n) { return n.length; }               // (A)
    static int b(String s, int... n) { return n.length; }     // (B)
    static int c(int... n, String s) { return n.length; }     // (C)

    public static void main(String[] args) {
        System.out.println("" + a() + b("x", 7));
    }
}

正解は「(C) の行でコンパイルエラーになる」です。可変長引数 (型のあとに ... を付けた仮引数) は 1 つのメソッドに 1 つだけで、仮引数の並びの最後に置かなければなりません。(C) の c は int... n のあとに String s を置いているので、(C) の行でコンパイルエラーになります。(A) のように可変長引数だけのメソッドは正しく、a() のように実引数を 1 つも渡さなければ長さ 0 の配列を受け取ります。(B) のように普通の仮引数のあとに可変長引数を置くのも正しい書き方です。c の宣言がエラーなので、「エラーにならず 0⁠1 と表示される」にもなりません。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.1

問7次の 2 つのクラスはどちらもコンストラクタを書いていません。コンパイラが自動で足す既定のコンストラクタのアクセス修飾子の組み合わせとして正しいものはどれですか。Report と Memo は同じパッケージの別のファイルにあります。
// Report.java
public class Report {
}

// Memo.java
class Memo {
}

正解は「Report は public、Memo は修飾子なし」です。クラスにコンストラクタの宣言が 1 つも無いときだけ、コンパイラは引数の無い既定のコンストラクタを足します。その既定のコンストラクタは、クラスと同じアクセス修飾子を持ちます。クラスに修飾子が無いときは、既定のコンストラクタも修飾子なし (パッケージ・アクセス) です。よって public class Report には public の、class Memo には修飾子なしのコンストラクタが足されます。リフレクションで修飾子を出すと Report は public、Memo は空の文字列になります。「どちらも public」は既定のコンストラクタがいつも public だと考えた誤り、「どちらも修飾子なし」はクラスの修飾子が引き継がれることを見落とした誤り、「protected と private」はどちらの決まりにも当たりません。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.8.9

問8次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Timer {
    int sec;

    Timer() {
        this(60);
    }

    Timer(int sec) {
        this();
        this.sec = sec;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new Timer().sec);
    }
}

正解は「コンパイルエラーになる」です。コンストラクタの中の this(...) は、同じクラスの別のコンストラクタを呼びます。ただし、this(...) の連なりで自分自身を直接または間接に呼ぶと、コンパイルエラーになります。このコードでは Timer() が this(6⁠0) で Timer(int) を呼び、Timer(int) が this() で Timer() を呼ぶので、2 つが互いに呼び合う輪になります。javac はこれを recursive constructor invocation として見つけ、実行する前に止めます。「実行時に StackOverflowError が出る」は、普通のメソッドの無限の再帰と同じに考えた誤りです。「6⁠0 と表示される」「0 と表示される」は、コンパイルが通る前提の誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.8.7

問9次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Gauge {
    int a = 5;

    {
        a += 1;
    }

    Gauge() {
        a *= 10;
    }

    {
        a -= 2;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new Gauge().a);
    }
}

正解は「4⁠0」です。new でオブジェクトを作ると、まずフィールドが既定値になり、次にフィールドの初期化子とインスタンス初期化ブロックが、ソースに書いた上からの順に動きます。コンストラクタの本体はそのあとです。順に追うと、a = 5 で 5、1 つ目のブロックの a += 1 で 6、2 つ目のブロックの a -= 2 で 4、最後にコンストラクタの a *= 1⁠0 で 4⁠0 になります。2 つ目のブロックはコンストラクタより下に書いてありますが、書いた位置に関係なくコンストラクタの本体より先に動きます。「5⁠8」はコンストラクタを書いた位置で動くと考えた誤り (6 から 6⁠0、そこから 5⁠8)、「6⁠0」は 2 つ目のブロックを見落とした誤り、「4」はコンストラクタの本体を見落とした誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠2.html#jls-⁠1⁠2.5

問10次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static void show(Long x) { System.out.println("Long"); }
    static void show(Object x) { System.out.println("Object"); }
    static void show(int... x) { System.out.println("int..."); }

    public static void main(String[] args) {
        show(5);
    }
}

正解は「Object」です。オーバーロードの解決は 3 つの段で進みます。1 段目は拡大変換までで合うメソッド、2 段目はボクシングとアンボクシングも許して合うメソッド、3 段目で初めて可変長引数のメソッドを見ます。前の段で見つかれば、後の段は見ません。int の 5 は、1 段目では Long にも Object にも合いません。2 段目では 5 を Integer にボクシングし、Integer は Object に代入できるので show(Object) が合います。Integer は Long に変換できないので show(Long) は合いません。2 段目で見つかったので、3 段目の show(int...) は選ばれません。よって「Object」です。「Long」は int を Long にボクシングできると考えた誤り、「int...」は型の近さで可変長を選ぶと考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.1⁠2.2

問11次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static void show(int a, long b) { System.out.println("int,long"); }
    static void show(long a, int b) { System.out.println("long,int"); }

    public static void main(String[] args) {
        show(1, 2); // (A)
    }
}

正解は「(A) の行でコンパイルエラーになる」です。show(1, 2) の 1 と 2 はどちらも int です。show(int, long) は 2 つ目を long に拡大すれば合い、show(long, int) は 1 つ目を long に拡大すれば合うので、2 つとも同じ段で呼び出せます。このときコンパイラは、より限定的なメソッドを 1 つに絞ろうとします。しかし int と long の向きが互い違いなので、どちらも他方より限定的とは言えません。1 つに決められない呼び出しはあいまい (ambiguous) とされ、呼び出しの (A) の行でコンパイルエラーになります。「int,long と表示される」「long,int と表示される」は、宣言の順や引数の位置で片方が選ばれると考えた誤りです。あいまいさはコンパイルの段階で見つかるので、実行時の例外にもなりません。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.1⁠2.2.5

static、アクセス修飾子、パターン・マッチング、レコード

問12次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Visit {
    static int total;

    Visit() {
        total++;
    }
}

public class Main {
    public static void main(String[] args) {
        new Visit();
        new Visit();
        Visit v = null;
        System.out.println(v.total);
    }
}

正解は「2」です。static のフィールドは、インスタンスをいくつ作ってもクラスに 1 つだけあり、すべてのインスタンスで共有されます。new Visit() を 2 回呼ぶとコンストラクタが 2 回動き、共有の total は 2 になります。static のフィールドを v.total のように参照型の式で読むと、使われるのは式の型 Visit だけで、v が指すオブジェクトは見ません。そのため v が null でも NullPointerException は出ず、Visit.total と同じ 2 が表示されます。「0」は v ごとに別の total があると考えた誤り、「NullPointerException」はインスタンスのフィールドと同じに考えた誤りです。変数を通した static の参照は勧められない書き方ですが、コンパイルエラーにはなりません。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.1⁠1.1

問13次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static final int LIMIT;                  // (A)
    static final String NAME = "box";

    static {
        LIMIT = NAME.length();               // (B)
    }

    public static void main(String[] args) {
        System.out.println(LIMIT + " " + NAME);
    }
}

正解は「3 box と表示される」です。宣言のときに値を書かない final のフィールドを、空の final (blank final) と呼びます。static の付いた空の final は、そのクラスの static 初期化子の中で必ず 1 回値を入れれば、コンパイルできます。(A) の LIMIT は宣言で値を入れていませんが、(B) の static 初期化子で値が入るのでエラーになりません。static 初期化子は上から順に動くので、(B) の時点で NAME はもう box で、LIMIT には長さの 3 が入ります。よって「3 box と表示される」です。「(A) の行でコンパイルエラーになる」は、static final は宣言と同時に値を入れなければならないと考えた誤りです。「(B) の行でコンパイルエラーになる」は、final には代入できないと考えた誤りで、まだ値の無い空の final への最初の代入は許されます。「0 box と表示される」は、既定値の 0 のまま残ると考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.3.1.2

問14次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Config {
    static int level = 5;

    static {
        System.out.print("S");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.print("A");
        Config c = null;
        System.out.print("B");
        System.out.print(Config.level);
    }
}

正解は「ABS5」です。クラスの static 初期化子は、プログラムの開始時ではなく、そのクラスが初めて使われる直前に 1 回だけ動きます。初めて使われるとは、インスタンスを作るとき、static のメソッドを呼ぶとき、定数でない static のフィールドを読み書きするときなどです。main ではまず A を表示します。Config c = null は Config 型の変数を宣言しただけで、クラスを使ったことにならないので初期化は起きず、続けて B を表示します。Config.level で定数でない static のフィールドを読むので、ここで初期化が起き、static 初期化子が S を表示してから 5 を表示します。よって「ABS5」です。「SAB5」は起動時に初期化されると考えた誤り、「ASB5」は変数の宣言で初期化されると考えた誤り、「AB5」は static 初期化子を見落としています。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠2.html#jls-⁠1⁠2.4.1

問15Tool と Main は同じファイルにあり、同じパッケージに属します。次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Tool {
    public int a = 1;
    protected int b = 2;
    int c = 3;
    private int d = 4;
}

public class Main {
    public static void main(String[] args) {
        Tool t = new Tool();
        int sum = 0;
        sum += t.a; // (A)
        sum += t.b; // (B)
        sum += t.c; // (C)
        sum += t.d; // (D)
        System.out.println(sum);
    }
}

正解は「(D) の行でコンパイルエラーになる」です。private のメンバーは、それを宣言したトップレベルのクラスの本体の中からだけ使えます。d は Tool の private なので、別のトップレベルのクラス Main の (D) の行からは使えず、d has private access in Tool というエラーになります。同じファイルにあっても、別のクラスなら使えません。public の a はどこからでも使えます。修飾子なし (パッケージ・アクセス) の c は同じパッケージから使えます。protected の b は、サブクラスからだけでなく同じパッケージのクラスからも使えるので、(B) もエラーになりません。「(B)」は protected をサブクラス専用と考えた誤り、「(C)」は修飾子なしを private と同じに考えた誤り、「1⁠0 と表示される」は private の決まりを見落としています。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠6.html#jls-⁠6.6.1

問16次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    static String label(Object o) {
        if (!(o instanceof Integer n)) {
            return "other";
        }
        return "num" + (n + 1);
    }

    public static void main(String[] args) {
        System.out.println(label(41) + " " + label("41"));
    }
}

正解は「num4⁠2 other」です。o instanceof Integer n は、o が Integer のとき真になり、パターン変数 n にキャストした値を入れます。!(...) で否定しているので、if の条件が真になるのは o が Integer でないときです。その if の本体は return で必ず抜けるので、if のあとに進めるのは o が Integer だったときだけです。言語仕様はこの形のとき、n を if 文のあとでも使えるようにしています。label(4⁠1) は if を素通りし、n + 1 を先に括弧で計算して num4⁠2 を返します。label("4⁠1") は String なので other を返します。よって「num4⁠2 other」です。「num4⁠1⁠1 other」は括弧があるのに文字列の連結が先だと考えた誤りです。「コンパイルエラーになる」は n が if の中でしか使えないと考えた誤り、「other other」は 4⁠1 が Integer に合わないと考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠6.html#jls-⁠6.3.2.2

問17次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
record Range(int low, int high) {
    Range {
        if (low > high) {
            int t = low;
            low = high;
            high = t;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Range r = new Range(9, 3);
        System.out.println(r.low() + "-" + r.high());
    }
}

正解は「3-⁠9」です。Range { ... } のように仮引数の並びを書かないコンストラクタは、レコードのコンパクト・コンストラクタです。仮引数は暗黙に宣言され、本体の中の low と high はフィールドではなくその仮引数を指します。本体が終わったあと、コンパイラが仮引数の値をフィールドに入れます。そのため本体で仮引数を入れ替えると、入れ替えた値がフィールドに入ります。new Range(9, 3) では low > high なので入れ替えが起き、low() は 3、high() は 9 を返します。よって「3-⁠9」です。「9-⁠3」は仮引数の入れ替えがフィールドに届かないと考えた誤りです。コンパクト・コンストラクタで禁じられているのは this.low = low のようにフィールドへ代入することで、仮引数への代入は書けるので「コンパイルエラーになる」も誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1⁠0.4.2

問18次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
record Book(String title, int pages) { }

public class Main {
    public static void main(String[] args) {
        Book b = new Book("Java", 300);
        System.out.println(b.title() + " / " + b);
    }
}

正解は「Java / Book[title=Java, pages=3⁠0⁠0]」です。レコードの各コンポーネントには、同じ名前で引数の無い public のアクセサ・メソッドが自動で作られます。名前は getTitle ではなく title() です。また toString も自動で作られ、クラスの名前とコンポーネントの名前と値を含む文字列を返します。JDK 1⁠7 では Book[title=Java, pages=3⁠0⁠0] の形になります (API 文書は、細かい書式は将来変わりうるとしています)。文字列の連結の中の b は toString() で文字列になるので、「Java / Book[title=Java, pages=3⁠0⁠0]」と表示されます。「Book@ のあとに 1⁠6 進の値」は Object の toString のままだと考えた誤り、「{title=Java, pages=3⁠0⁠0}」は Map の表示との混同、「コンパイルエラーになる」はアクセサの名前を getTitle だと考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1⁠0.3

問19次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
record Point(int x, int y) { }

public class Main {
    public static void main(String[] args) {
        Point a = new Point(1, 2);
        Point b = new Point(1, 2);
        System.out.println((a == b) + " " + a.equals(b) + " " + (a.hashCode() == b.hashCode()));
    }
}

正解は「false true true」です。a と b は new で別々に作ったオブジェクトなので、参照を比べる a == b は false です。レコードの equals は自動で作られ、同じレコード・クラスで、すべてのコンポーネントの値が等しいときに true を返します。x と y がどちらも 1 と 2 で等しいので a.equals(b) は true です。hashCode も各コンポーネントから計算されるように自動で作られ、equals で等しいオブジェクトは同じハッシュ値になるので true です。よって「false true true」です。「true true true」はレコードでは == も中身を比べると考えた誤り、「false false false」と「false false true」は equals が Object のままで参照を比べると考えた誤りです。 出典 https://docs.oracle.com/en/java/javase/1⁠7/docs/api/java.base/java/lang/Record.html#equals(java.lang.Object)

問20次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
record Counter(int value) {
    void reset() {
        value = 0;              // (A)
    }

    int next() {
        return value + 1;       // (B)
    }
}

public class Main {
    public static void main(String[] args) {
        Counter c = new Counter(5);
        c.reset();
        System.out.println(c.next());
    }
}

正解は「(A) の行でコンパイルエラーになる」です。レコードのコンポーネントごとに作られるフィールドは private で final です。final のフィールドは、コンストラクタで一度値が入ったあとは書き換えられません。reset の中の value = 0 はこの final のフィールドへの代入なので、(A) の行で cannot assign a value to final variable value というエラーになります。(B) の value + 1 はフィールドを読むだけなので問題ありません。レコードの値を変えたいときは、新しい値で別のレコードを作ります。「1 と表示される」は reset が値を 0 にできると考えた誤り、「6 と表示される」はコンパイルが通る前提の誤り、「(B)」は読むことまで禁じられると考えた誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1⁠0.3

問21次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
record Item(String name, int price) {
    static int created;                                      // (A)

    static Item free(String n) { return new Item(n, 0); }    // (B)

    int withTax() { return price * 110 / 100; }              // (C)

    int stock;                                               // (D)
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Item.free("pen").withTax());
    }
}

正解は「(D) の行でコンパイルエラーになる」です。レコードの本体には、static のフィールド、static のメソッド、インスタンス・メソッド、コンストラクタ、入れ子の型などを書けます。しかし static でないフィールド (インスタンス変数) は書けません。レコードの状態はヘッダーのコンポーネントだけで表す決まりだからです。(D) の int stock は static の付かないフィールドなので、(D) の行でコンパイルエラーになります。(A) の static int created は static のフィールドなので書けます。(B) の free は static のメソッドで、正規のコンストラクタを呼んで新しいレコードを返しています。(C) の withTax はインスタンス・メソッドで、コンポーネントの price を読んでいるだけなので書けます。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1⁠0.2

問22次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
record Size(int w, int h) { }

class Square extends Size {         // (A)
    Square(int s) { super(s, s); }  // (B)
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new Square(2).w());
    }
}

正解は「(A) の行でコンパイルエラーになる」です。レコード・クラスは暗黙に final です。final のクラスはほかのクラスの extends に書けないので、class Square extends Size の (A) の行で cannot inherit from final Size というエラーになります。レコードの直接のスーパークラスは java.lang.Record に決まっていて、レコード自身にも extends は書けません。そのため、レコードは継承する側にもされる側にもなれません (インタフェースの implements は書けます)。(B) の super(s, s) は Size に合うコンストラクタがあるので、Size を継承できればここでエラーにはなりません。「エラーにならず 2 と表示される」と「実行時に例外が出る」はコンパイルが通る前提の誤りです。 出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1⁠0

ほかの分野

Java Silverの頁に、6分野の入口と試験の案内があります。

続きはゆめさくで

試験の知識を仕事につなげる一歩を、ここにまとめています。