クラスの定義とインスタンスの使用の要点
内容の確認日: 2026-10-04
この分野は出題項目が 7 つと多く、Silver の中でも特に範囲の広い分野です。Bronze で学んだクラス・コンストラクタ・static に加えて、初期化の順番、オーバーロードの解決規則、protected を含むアクセス修飾子、そして Java SE 16 で正式に入った instanceof のパターン・マッチングとレコードが問われます。
出題項目は「Java クラスの定義とインスタンス化とオブジェクトのライフサイクル」「メソッドとコンストラクタの作成」「メソッドのオーバーロード」「static 変数と static メソッド」「アクセス修飾子の使用」「instanceof のパターン・マッチング」「レコード・クラスの作成と使用」です。
オブジェクトのライフサイクルとガベージコレクション
new で作ったオブジェクトは、どの変数からも (たどれる参照の連鎖からも) 参照されなくなると、ガベージコレクションの対象になります。対象になったオブジェクトがいつ回収されるかは JVM が決めるので、プログラムからは分かりません。System.gc() も回収を要求するだけです。
試験では、参照の代入を 1 行ずつ追って「この時点で対象になるオブジェクトはいくつか」を答える問題が出ます。変数ごとに「どのオブジェクトを指しているか」を矢印で書き、どこからも矢印が来ていないオブジェクトを数えましょう。
Item a = new Item("1"); // a → (1)Item b = new Item("2"); // b → (2)b = a; // b → (1)。(2) は参照されなくなり、対象になるa = null; // (1) はまだ b から参照されている初期化子とコンストラクタの実行順
クラスの中に直接書いた { } をインスタンス初期化子、static { } を static 初期化子と呼びます。実行の順番は次のとおりです。
1. static 初期化子と static 変数の初期化: クラスが最初に使われるときに 1 回だけ、書いた順に実行
2. インスタンス変数の初期化とインスタンス初期化子: new のたびに、書いた順に実行
3. コンストラクタの本体
class Sample { static { System.out.println("static 初期化子"); } int x = init("フィールドの初期化"); { System.out.println("インスタンス初期化子"); } Sample() { System.out.println("コンストラクタ"); } static int init(String s) { System.out.println(s); return 1; }}public class Main { public static void main(String[] args) { System.out.println("main 開始"); new Sample(); new Sample(); }}java Main main 開始 static 初期化子 フィールドの初期化 インスタンス初期化子 コンストラクタ フィールドの初期化 インスタンス初期化子 コンストラクタ
static 初期化子は Sample を最初に使った時点 (1 回目の new) で 1 回だけ実行され、main の開始より後になる点に注目してください。
可変長引数とオーバーロードの解決
int... nums のような可変長引数は、メソッドの中では int[] として扱われます。引数なしで呼ぶと要素数 0 の配列になります。可変長引数は、引数リストの最後に 1 つだけ置けます。
オーバーロードされたメソッドのどれが呼ばれるかは、次の順で決まります。上の段階で見つかれば、下の段階は検討されません。
1. 型が完全に一致するもの
2. プリミティブ型の拡大変換 (int → long など) で呼べるもの
3. オートボクシング (int → Integer など) で呼べるもの
4. 可変長引数で呼べるもの
public class Main { static void call(long n) { System.out.println("long"); } static void call(Integer n) { System.out.println("Integer"); } static void call(int... n) { System.out.println("int... (" + n.length + ")"); } public static void main(String[] args) { call(1); call(Integer.valueOf(1)); call(1, 2); call(); }}java Main long Integer int... (2) int... (0)
call(1) には int に完全一致するメソッドがないので、拡大変換の long 版が、ボクシングの Integer 版より優先されます。また、null を渡すと、String 版と StringBuilder 版のように継承関係のない参照型のオーバーロードがある場合は、どちらとも決められずコンパイルエラーになります。
static メンバー
static 変数・static メソッドはクラスに属します。変数.メソッド() のように参照変数を通して書いても、コンパイラは変数の型から クラス名.メソッド() として呼び出します。そのため、変数が null でも static メソッドは呼べてしまいます。
class Counter { static int total = 0; static void add() { total++; }}public class Main { public static void main(String[] args) { Counter c = null; c.add(); Counter.add(); System.out.println(Counter.total); }}java Main 2
static メソッドの中では this が使えず、インスタンス変数・インスタンスメソッドを名前だけで使うこともできません (コンパイルエラー)。使いたいときは、インスタンスを作って obj.count のように指定します。
アクセス修飾子
アクセス修飾子は 4 段階で、公開範囲の広い順に次のとおりです。
・public: すべてのクラス
・protected: 同じパッケージのクラスと、別パッケージのサブクラス
・修飾子なし (パッケージ・プライベート): 同じパッケージのクラスだけ
・private: 同じクラスの中だけ
protected で注意したいのは、別パッケージのサブクラスからアクセスできるのは「継承した自分 (やそのサブクラス) のメンバー」としてだけ、という点です。サブクラスの中でも、new で作った親クラスのインスタンスの protected メンバーにはアクセスできません。
トップレベルのクラス (ファイルの一番外側のクラス) に付けられるのは、public か修飾子なしだけです。private や protected は付けられません。
instanceof のパターン・マッチング
Java SE 16 からは、obj instanceof String s のように型の後ろに変数名を書くと、obj が String なら true になり、同時に String 型にキャストした値が変数 s に入ります (パターン変数)。キャストを別に書く必要がありません。
パターン変数が使えるのは、instanceof が true だと確定している場所だけです。&& の右側や、if のブロックの中では使えます。if (!(o instanceof Integer i)) return ...; のように否定して先にメソッドを抜けた場合は、if 文の後ろで使えます。
public class Main { static String describe(Object o) { if (o instanceof String s && !s.isEmpty()) { return "文字列: " + s.length() + " 文字"; } if (!(o instanceof Integer i)) { return "その他"; } return "整数: " + (i * 2); } public static void main(String[] args) { System.out.println(describe("Java")); System.out.println(describe(21)); System.out.println(describe("")); System.out.println(describe(3.0)); }}java Main 文字列: 4 文字 整数: 42 その他 その他
|| の右側は、左側が false のとき (つまり instanceof が成り立たなかったとき) に評価されるので、パターン変数は使えません。
public class Main { public static void main(String[] args) { Object o = "x"; if (o instanceof String s || s.isEmpty()) { System.out.println("ok"); } }}javac Main.java
コンパイルエラー (4 行目): || の右側では、パターン変数 s が使えない
レコード・クラス
Java SE 16 からのレコードは、値をまとめて持つだけのクラスを簡潔に書くための仕組みです。record Point(int x, int y) { } と書くと、次のものが自動で作られます。
・private final のフィールド x と y
・すべての値を受け取るコンストラクタ (カノニカル・コンストラクタ)
・値を返すアクセサメソッド x() と y() (getX() ではない)
・値で比較する equals・hashCode と、Point[x=1, y=2] の形の toString
引数リストを省略したコンパクト・コンストラクタ (Point { ... }) を書くと、引数の値をチェックしたり調整したりできます。最後に、引数の値が自動でフィールドに代入されます。
record Point(int x, int y) { Point { if (x < 0) x = 0; } int sum() { return x + y; }}public class Main { public static void main(String[] args) { Point p = new Point(-5, 3); System.out.println(p); System.out.println(p.x() + " " + p.sum()); System.out.println(p.equals(new Point(0, 3))); }}java Main Point[x=0, y=3] 0 3 true
レコードは暗黙的に java.lang.Record を継承した final クラスです。そのため extends は書けず、サブクラスも作れません (インタフェースの implements はできます)。また、インスタンスフィールドを追加で宣言することはできません (static フィールドは可)。フィールドは final なので、作った後で値を変えることもできません。
試験でよくある引っかけ
static初期化子の実行回数static初期化子は、クラスが最初に使われたときに 1 回だけ実行されます。インスタンス初期化子はnewのたびに、コンストラクタより先に実行されます。オーバーロードの優先順位
完全一致 → 拡大変換 → ボクシング → 可変長引数の順です。
call(1)はInteger版よりlong版が選ばれます。null参照からのstaticメソッドCounter c = null; c.add();は、addがstaticなら例外にならず実行されます。protectedと別パッケージ別パッケージのサブクラスからは、継承したメンバーとしてだけ
protectedにアクセスできます。パターン変数のスコープ
o instanceof String s || s.isEmpty()はコンパイルエラーです。&&の右側なら使えます。レコードのアクセサ名
record Point(int x, int y)のアクセサはx()とy()です。getX()は自動では作られません。
問題で確認する
この分野の問題を、解説つきで読めます (登録不要)。
出典・参考
- Oracle University 日本「Java SE 17 Programmer I (1Z0-825-JPN) 試験」
- JEP 394: Pattern Matching for instanceof
- JEP 395: Records
- Java 言語仕様 (Java SE 17) 15.12.2「Compile-Time Step 2: Determine Method Signature」
コード例は、試験の対象バージョンで実際にコンパイル・実行して結果を確認しています。