Java silver se17に2025年10月に合格した(合格体験記はこちら)。その勉強の際に下に示した黒本で勉強した内容をもとに問題集を作り何度も反復していた。
この問題集を眠らすのはもったいないと感じブログに載せることにした(大幅に改変した)。
志賀 澄人 徹底攻略Java SE 17 Silver問題集[1Z0-825]対応 を参考にして作成。この黒本の範囲を網羅しているはずだ。黒本に載ってなかったり、本番でも出なかった内容は省いて作成した。
また、本ブログのJava silver se17対策問題集1~6で合計60問あり、本番の問題数と一致しているので模試として使えるかも??
解説はGeminiを使って作成し、私が確認。JavaはJava silverのための勉強と、スクールでの勉強しかしていない。解説が完璧でない点はご容赦いただきたい。
ただ、コードはEclipseで確認しているので問題の正誤はあっているはずだ。
1:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Main {int i;
static void a() {
System.out.print(i--);
}
public static void main(String[] args) {
a();Main.a();
}
}
A:00
B:0-1
C:-1-2
D:コンパイルエラー
E:実行時に例外がスローされる
正解を確認する
static メソッドである a() の中で、インスタンス変数である i を参照しようとしているためです。
2:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Main {int i;
protected Main(){
i=20;
}
protected void a() {
System.out.print(this.i);
System.out.print(i);
}
public static void main(String[] args) {
Main a=new A();a.a();
A b=new A();b.a();
}
}
class A extends Main{int i=1;
A(){
i=10;
}
}
A:0000
B:20202020
C:20201020
D:0010
E:コンパイルエラー
F:実行時に例外がスローされる
正解を確認する
最初のインスタンス (Main a = new A();) new A() が呼ばれる。 A()コンストラクタが実行される前に、暗黙的に親クラスのMain()コンストラクタが呼ばれる。 Main.i に 20 が代入される。 A()コンストラクタが実行される。 A.i に 10 が代入される。 a.a() が呼ばれる。 実行されるのは Main.a()。 this.i はMainクラスのフィールドを参照するため、20 を出力。 i はMainクラスのフィールドを参照するため、20 を出力。 2番目のインスタンス (A b = new A();) new A() が呼ばれる。 上記と同様に、新しいオブジェクトの Main.i は 20、A.i は 10 に設定される。 this.i および i はMainクラスのフィールドを参照するため、それぞれ20 を出力。
3:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Main {
static Number []i[]=new Short[3][3];
public static void main(String[] args) {
Main a=new Main();a.i[1]= new Short[]{1};a.i[2]=new Short[]{null,2};
Main b=new Main();
int sum=0;
for(Number j[]:b.i) {
for(Number k:j) {
if(k==null)sum+=100;
else sum+=(Short)k;
}
}
System.out.println(sum);
}
}
A:403
B:900
C:0
D:3
E:コンパイルエラー
F:実行時に例外がスローされる
正解を確認する
1. 配列の初期状態 (静的フィールド i)
static Number []i[]=new Short[3][3]; により、静的フィールド i は Short[3][3] のオブジェクトを参照します。
すべての要素は参照型のデフォルト値である null で初期化されます。
i=[{null,null,null},{null,null,null},{null,null,null}]
2. 配列の変更 (aの処理)
a も b も同じ静的フィールド i を共有します。代入により、i[1] と i[2] が新しい配列に置き換えられます。
a.i[1] = new Short[]{1};
a.i[2] = new Short[]{null, 2};
i=[{null,null,null},{1},{null, 2}]
3. ループ処理 (bの処理)
b.i は上記の配列を参照し、合計値 sum を計算します。
| 処理ステップ | j (内側の配列) | k の値 | k == null | 処理 (sum += ...) | sum の合計 |
|---|---|---|---|---|---|
| i[0] | {null, null, null} | null | True | 100 | 0 → 100 |
| null | True | 100 | 100 → 200 | ||
| null | True | 100 | 200 → 300 | ||
| i[1] | {1} | 1 | False | (Short)1 (アンボクシング) | 300 → 301 |
| i[2] | {null, 2} | null | True | 100 | 301 → 401 |
| 2 | False | (Short)2 (アンボクシング) | 401 → 403 | ||
| 最終的な sum の値: 403 | |||||
4:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Main {
public static void main(String[] args) {
Object i[][]= {{null},{1}};
Object j[]=i[1].clone();Object [][]k=i.clone();
j[0]=2.0;k[1][0]=null;
System.out.print(""+(i[1][0]==j[0])+(i[1][0]==k[1][0]));
k[0]=new Integer[] {3,4};
System.out.print((k[0]==i[0])+""+(k==i)+k.equals(i));
}
}
A:falsefalsefalsetruetrue
B:falsefalsefalsefalsefalse
C:falsetruefalsefalsefalse
D:falsetruefalsetruetrue
E:truetruetruefalsefalse
F:コンパイルエラー
正解を確認する
1. 初期状態とクローン
Object i[][]= {{null},{1}};
Object j[]=i[1].clone();
Object [][]k=i.clone();
i: 2次元配列。
i[0] は配列 {null} を参照。
i[1] は配列 {Integer(1)} を参照。
j: 新しい配列ですが、j[0] は i[1][0] と同じ Integer(1) オブジェクトを参照します。
k: 新しい配列ですが、その要素 k[0] は i[0] と同じ配列を、k[1] は i[1] と同じ配列を参照します。
2. 最初の変更
j[0]=2.0;
k[1][0]=null;
j[0]=2.0;:
j[0] は新しい Double(2.0) オブジェクトを参照するようになります。i[1][0] への影響はありません。
k[1][0]=null;:
k[1] と i[1] は同じ配列オブジェクトなので、i[1][0] の値も null に上書きされます。
3. 最初の出力 (i[1][0]==j[0] と i[1][0]==k[1][0])
System.out.print(""+(i[1][0]==j[0])+(i[1][0]==k[1][0]));
(i[1][0] == j[0]):
i[1][0] は null、j[0] は Double(2.0) (非null)。
null == Object は false。
(i[1][0] == k[1][0]):
i[1][0] は null、k[1][0] は null。
null == null は true。
出力 (前半): falsetrue
4. 2回目の変更
k[0]=new Integer[] {3,4};
k[0] が、新しい配列 {3, 4} を参照するようになります。
i[0] は元の配列 {null} を参照したままです(i[0]とk[0]は異なる配列になりました)。
5. 2回目の出力 (k[0]==i[0], k==i, k.equals(i))
System.out.print((k[0]==i[0])+""+(k==i)+k.equals(i));
(k[0] == i[0]):k[0] は新しい配列、i[0] は古い配列で、参照先は異なります。よってfalse。
(k == i):k は i のトップレベルの配列のクローン(別オブジェクト)です。よってfalse。
k.equals(i):配列の equals() メソッド(Objectから継承)は、== と同じく参照比較を行います。k と i は異なるオブジェクトなので、false。
出力 (後半): falsefalsefalse
5:次のレコード内に記述できるコードとして正しいものはどれか。
public record A(String i) {
}
A:A(){}
B:A(String i){this.i=i;System.out.print(“Hi”);}
C:A(){this(“anything”);}
D:A、B、Cすべて
E:BとC
F:上記のいずれも正しくない
正解を確認する
レコードのルールにより、コンストラクタのアクセス修飾子は、レコード自身のアクセス修飾子(今回はpublic)よりも制限的であってはなりません。したがって、public修飾子のないA~Cはすべてコンパイルエラーとなります。
6:次のコード断片をコンパイルしたときline A, line B, line Cのどこでコンパイルエラーが生じるか。
record A(String i,int j) {
A(){ //line A
this.i=null;this.j=0;
}
A(String i,int k){ //line B
this.i=i==null?"Hi":i;j=k<0?0:k;
}
A(int j){ //line C
this(null,3);
}
}
A:コンパイルエラーは生じない
B:A,B,C
C:A
D:B
E:C
F:B,C
G:A,C
正解を確認する
line A: コンパイルエラー これはカスタムコンストラクタ(非正規コンストラクタ)です。カスタムコンストラクタは、コンポーネントの初期化を確実に行うために、必ず最初の文で他のコンストラクタ(正規コンストラクタ)を呼び出し、初期化を委譲しなければなりません。 このコードは、委譲 (this(...)) を行わずに、コンストラクタ本体で直接フィールド (this.i=null;) に代入しようとしているため、コンパイルエラーとなります。 line B: コンパイルエラーなし (正規コンストラクタの正しい定義) これはレコードが自動生成する正規コンストラクタと同じシグネチャを持ちますが、本文でコンポーネントへの代入を明示的に行っているため、この形式は正規コンストラクタの定義としては有効です。代入が行われているため、バリデーションやデータ変換(この場合は null チェックや負値チェック)のロジックを含めて値を設定できます。引数名は異なっても(j と k)、型と順序が一致していれば正規コンストラクタとして認識されます。 line C: コンパイルエラーなし これはカスタムコンストラクタですが、最初の文で正規コンストラクタを呼び出し(this(null, 3))、コンポーネントの初期化を正しく委譲しているため、レコードのルールに則った有効な記述です。
7:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Main {
public static void main(String[] args) {
System.out.println(a());
}
private static String a() {
try {
throw new Exception();
} catch (Throwable t) {
System.out.print("catch ");
return "CR ";
} finally {
System.out.print("finally ");
return "FR ";
}
}
}
A:catch finally CR
B:catch finally FR
C:catch CR finally
D:catch FR finally
E:コンパイルエラー
F:実行時に例外がスローされる
正解を確認する
この問題は、Javaにおける try-catch-finally ブロックの実行順序と、finally ブロックからの return が持つ特殊な優先順位を問うものです。
1.try ブロックの実行
メソッド a() が呼び出され、try ブロックに入ります。
throw new Exception(); により、例外がスローされます。
try ブロックの実行は中断されます。
2.catch ブロックの実行
投げられた Exception は、catch (Throwable t) で捕捉されます。
捕捉された後、以下の処理が順に実行されます。
System.out.print("catch "); が実行されます。
return "CR "; が実行されます。ここで、メソッドの戻り値 "CR " は確定しますが、直ちにメソッドを終了しません。
3.finally ブロックの実行
try または catch ブロックがどのように終了したかに関わらず、finally ブロックが実行されます。
System.out.print("finally "); が実行されます。
return "FR "; が実行されます。
4.メソッドの終了
Javaのルールでは、finally ブロックに return 文がある場合、その値は**catch ブロックで確定した戻り値を上書きし**、finally ブロックの return が最終的なメソッドの戻り値となります。
a() メソッドは "FR " を返し、main メソッドでそれが出力されます。
正解を確認する
この問題は、Javaにおける try-catch-finally ブロックの実行順序と、finally ブロックからの return が持つ特殊な優先順位を問うものです。
1.try ブロックの実行
メソッド a() が呼び出され、try ブロックに入ります。
throw new Exception(); により、例外がスローされます。
try ブロックの実行は中断されます。
2.catch ブロックの実行
投げられた Exception は、catch (Throwable t) で捕捉されます。
捕捉された後、以下の処理が順に実行されます。
System.out.print("catch "); が実行されます。
return "CR "; が実行されます。ここで、メソッドの戻り値 "CR " は確定しますが、直ちにメソッドを終了しません。
3.finally ブロックの実行
try または catch ブロックがどのように終了したかに関わらず、finally ブロックが実行されます。
System.out.print("finally "); が実行されます。
return "FR "; が実行されます。
4.メソッドの終了
Javaのルールでは、finally ブロックに return 文がある場合、その値は**catch ブロックで確定した戻り値を上書きし**、finally ブロックの return が最終的なメソッドの戻り値となります。
a() メソッドは "FR " を返し、main メソッドでそれが出力されます。
8:次のコードをコンパイル、実行したときに0と出力したい。空欄■■■■■■■に何を入れるべきか。
public class Main {
int i;
public static void main(String[] args) {
int i=1;
System.out.println(■■■■■■■);
}
}
A:i
B:this.i
C:Main.i
D:new Main().i
E:BとD
F:B、C、D
正解を確認する
出力したい値 (0) の場所: Main クラスのインスタンス変数 int i; は、明示的に初期化されていないため、デフォルト値である 0 が保持されています。 したがって、0 を出力するには、このインスタンス変数 i にアクセスする必要があります。 A: i → main メソッド内のローカル変数(値は 1)を参照するため、1 が出力されます。 B: this.i → main メソッドは static であり、特定のインスタンスに紐づいていないため、this キーワードを使用できず、コンパイルエラーになります。 C: Main.i → i は インスタンス変数(非 static)であり、クラス名を使って直接アクセスすることはできないため、コンパイルエラーになります。 D: new Main().i → 新しい Main オブジェクトを一時的に作成し、そのオブジェクトのインスタンス変数 i にアクセスします。インスタンス変数 i の値はデフォルト値の 0 なので、0 が出力されます。 したがって、0 を出力できるのは new Main().i のみです。
9:次のコード断片のうちコンパイルエラーが生じないものはどれか。
A:void a() {try {throw new java.io.IOException();}finally {}
int j;}
B:void a() {try {throw new java.io.IOException();}catch(Exception i){}
int j;}
C:void a() throws Exception{try {throw new java.io.IOException();}finally{}
int j;}
D:A、B、C
E:BとC
F:コンパイルエラーが生じないものはない
正解を確認する
この問題は、Javaにおける到達不能なコードのルールに関係しています。コンパイラは、コードの行に処理が到達する可能性が全くないと判断した場合、エラーを発生させます。
A: コンパイルエラーが生じる
void a() {
try {
throw new new java.io.IOException(); // 検査例外
} finally {
}
int j; // 到達不能
}
try ブロックで検査例外 (IOException) が無条件にスローされています。
この例外を捕捉する catch ブロックがなく、メソッドシグネチャに throws 宣言もないため、このメソッドは例外を処理できず、例外が伝播する形で終了します。
したがって、try-finally ブロックを抜けた直後の int j; に処理が到達する正常な経路が全く存在しないとコンパイラが判断し、到達不能なコードとしてエラーになります。
B: コンパイルエラーが生じない
void a() {
try {
throw new java.io.IOException();
} catch(Exception i) { // 例外を捕捉
}
int j; // 到達可能
}
catch ブロックが IOException を捕捉し、例外処理を完了させます。
例外処理が完了した後、処理は try-catch ブロックを抜けて int j; に正常に続行する経路が確立されるため、コンパイルエラーは生じません。
C: コンパイルエラーが生じる
void a() throws Exception{
try {
throw new java.io.IOException(); // 無条件スロー
} finally {
}
int j; // 到達不能
}
throws Exception があるため、例外の委譲は合法ですが、try ブロック内で例外が無条件にスローされています。
処理が finally ブロックを通過した後、例外が呼び出し元に伝播してメソッドは終了します。
A の場合と同様に、try-finally ブロックを正常に抜けて int j; に到達する経路が全くないとコンパイラが判断するため、到達不能なコードとしてエラーになります。
10:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Main {
public static void main(String[] args) {
String i=String.valueOf("Hello");
Main(i);Main(i,"Hi");
System.out.println(i.toLowerCase());
}
static String Main(String i) {
return i+=" World";
}
public static void Main(String i,String j) {
i.replace("Hello", j);
}
}
A:hello World
B:hi World
C:hello
D:hi
E:コンパイルエラー
F:実行時に例外がスローされる
正解を確認する
このコードの振る舞いは、Javaにおけるメソッドのオーバーロード、String の不変性(Immutability)、そしてローカル変数への影響によって決まります。
1. 初期化
String i=String.valueOf("Hello");
main メソッドのローカル変数 i は、文字列 "Hello" を参照します。
2. 最初のメソッド呼び出し: Main(String i)
Main(i); // i = "Hello"
static String Main(String i) {
return i+=" World";
}
メソッド Main(String i) にローカル変数 i("Hello")の値が渡されます(値渡し)。
メソッド内では、i += " World" が実行されます。String は不変なので、これは新しい文字列 "Hello World" を作成します。
この新しい文字列 "Hello World" への参照は、メソッド内のローカル変数 i に格納され、そして return されます。
しかし、main メソッド内ではこの戻り値が受け取られていません。
したがって、main メソッド内のローカル変数 i は、元の値 "Hello" を参照したまま変化しません。
3. 2番目のメソッド呼び出し: Main(String i, String j)
Main(i,"Hi"); // i = "Hello"
public static void Main(String i,String j) {
i.replace("Hello", j);
}
メソッドに i("Hello")と "Hi" が渡されます。
i.replace("Hello", j) が実行されます。
replace() メソッドは、元の文字列を変更せず、新しい文字列 "Hi" を作成してその参照を返します。
しかし、この戻り値はどこにも格納されずに破棄されます。
したがって、main メソッド内のローカル変数 i は、ここでも値 "Hello" を参照したまま変化しません。
4. 最終的な出力
System.out.println(i.toLowerCase());
main メソッドのローカル変数 i は最後まで "Hello" のままです。
i.toLowerCase() は、"Hello" を小文字にした "hello" を返します。
最終的な出力は hello となります。
コメント