51:>java a/Test.java のコマンドで次のコードを実行したときの結果として正しいものを選べ。
package a;
public class Test extends jp.co.java.Test {
public static void main(String[] args) {
String i = "b";
String j = "a";
i = j;
i = null;
System.out.print(jp.co.java.Test.check(i) + jp.co.java.Test.check(j));
}
}
package jp.co.java;
class Main{
public static void main(String[] args) {
String i = "a";
string j = "b";
i = j;
}
}
public class Test {
protected static String check(String i) {
return switch(i) {
case "a" -> i.toString();
case "b" -> i.toLowerCase();
default -> null;
};
}
}
A:ab
B:nullnull
C:bb
D:nulla
E:コンパイルエラーが生じる
F:実行時に例外がスローされる
正解を確認する
Javaの switch 式(および switch 文)でString型文字列の値で条件分枝するときString.hashCode()メソッドが呼び出されます。switch(i) の i が null であるため、この行が評価された瞬間に NullPointerException がスローされます。
52:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
ppublic class Main{
public static void main(String[]args) {
A a=new A();
try (a){
A.put(a);
}finally {
System.out.println(""+a.i[0]+a.i[1]);
}
a=null;
}
}
class A implements AutoCloseable{
public char[]i= {'a','b'};
public void close() {
i[1]='a';
}
static void put(A a) {
a.i[0]='b';
}
}
A:aa
B:ab
C:ba
D:195
E:コンパイルエラーが生じる
F:実行時に例外がスローされる
正解を確認する
try の括弧 () の中で、外で宣言された既存の変数(ここでは a)を使用する場合、その変数は「final」または「事実上final (effectively final)」でなければならない、というルールがあります。 「事実上final」とは、変数が初期化された後、その参照先(値)が二度と変更されないことを意味します。 しかし、このコードでは try-finally ブロックの 後 に a=null; という行があり、変数 a が再代入されています。 この再代入があるため、変数 a は「事実上final」とはみなされず、try (a) の行でコンパイルエラーが発生します。 (仮にこれらのエラーが修正された場合、try ブロックの put メソッドで i[0] が 'b' になり、try 終了時に自動で close メソッドが呼ばれて i[1] が 'a' になり、finally で ba が出力されます)
53:次のコードを読み、class Aの定義として正しいものを2個選びなさい。
public class Sample {
public static void main(String[] k) {
throw new A();
}
}
A:class A extends java.io.IOException {}
B:class A {}
C:class A extends RuntimeException {}
D:class A implements AutoCloseable { public void close() {}}
E:class A {public void a() {throw new NumberFormatException();}}
F:class A extends Error {public void a() {throw new IllegalArgumentException();}}
F:いずれも正しくない
正解を確認する
このコードの main メソッド(public static void main(String[] k))は、throws キーワードによる例外のスロー宣言をしていません。 Javaのルールでは、メソッドが throws 宣言をしていない場合、そのメソッド内で throw できる(投げることができる)のは、「非チェック例外」に限られます。 「非チェック例外」とは、RuntimeException(実行時例外)または Error(エラー)、およびそれらのサブクラス(子クラス)のことです。throw new A(); というコードがコンパイルエラーにならないためには、A クラスが RuntimeException または Error のどちらかのサブクラスである必要があります。 A: IOException は RuntimeException ではない「チェック例外」です。これを throw するには main メソッドに throws IOException という宣言が必要です。 B, D, E: A が Throwable(例外やエラーの親クラス)を継承していません。throw キーワードで投げることができるのは Throwable のサブクラスだけです。 C: A は RuntimeException を継承しています。これは「非チェック例外」であるため、main メソッドに throws 宣言がなくても throw することができます。 F: A は Error を継承しています。これも「非チェック例外」であるため、main メソッドに throws 宣言がなくても throw することができます。
54:次のコードを読み、interface B、interface Cの定義として正しいものを選びなさい。
public interface A extends B, C {default char[] take() {}}
A:interface B {int move();}
interface C {int[] move();}
B:interface B {Collection move();}
interface C {Object move();}
C:interface B {char take();}
interface C {static void take(int…i) {}}
D:interface B { B() {}}
interface C {C() {}}
E:interface B {void move();}
interface C {Object move();}
F:いずれも正しくない
正解を確認する
A:A は int move() と int[] move() という、シグネチャ(名前と引数)が同じで戻り値の型が異なる二つの抽象メソッドを継承することになります。int と int[] に互換性はないため、A はコンパイルエラーになります。
B:A は Collection move() と Object move() を継承します。戻り値の型(Collection と Object)は互換性があります(Collection は Object のサブタイプ)。この場合、より具体的な Collection move() という抽象メソッドを A が継承することになります。これは構文的に有効であり、A はコンパイルエラーになりません。
C:Bの char take() と Aの default char[] take() {} が同じシグネチャ(take())で競合します。
AはBの抽象メソッドを実装することになりますが、Bの戻り値 char とAの戻り値 char[] は互いにサブタイプではないため、コンパイルエラーになります。
D:インターフェースはコンストラクタを持つことができません。B と C の定義自体がコンパイルエラーです。
E:A は void move() と Object move() という、シグネチャが同じで戻り値の型が異なる二つの抽象メソッドを継承します。void と Object に互換性はないため、A はコンパイルエラーになります。
55:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
package a$;
public class Test extends Test2{
public static void main(String[] args) {
check();//line a
cut();//line b
}
}
class Test2 {
protected static void check() {
System.out.println('a' + 'b');
}
}
class Test3 {
static void cut() {
System.out.println('a');
}
}
A:line aでコンパイルエラーが生じる
B:line bでコンパイルエラーが生じる
C:line a、line bでコンパイルエラーが生じる
D:195
a
E:195
97
F:aba
G:実行時に例外がスローされる
正解を確認する
check();
protected メンバーは、同じパッケージ内(この場合はパッケージ a$ 内)のクラス、またはサブクラス(子クラス)からアクセス可能です。Test クラスは Test2 を継承しており、同じパッケージ内にあります。したがって、check() メソッドをクラス名を付けずに呼び出すことは文法的に正しく、コンパイルエラーになりません。
cut();
Test3 は、呼び出し元の Test クラス(またはそのスーパークラス Test2)とは継承関係がない、独立したクラスです。
他のクラスに定義された static メソッドを呼び出す場合、通常、クラス名を明示的に指定する必要があります(例: Test3.cut();)。cut(); とだけ記述すると、コンパイラは呼び出し元のクラス(Test)またはその親クラス(Test2)の中だけで cut() メソッドを探します。Test と Test2 のいずれにも cut() メソッドは定義されていないため、コンパイラは line b で「シンボルが見つかりません (cannot find symbol)」というエラーを発生させます。
補足(もしコンパイルエラーが生じなかった場合の出力)
もし line b が Test3.cut(); に修正されてコンパイルが成功した場合、実行結果は以下のようになります。
System.out.println('a' + 'b'); の実行
Javaでは、char 型(文字型)の演算は、その文字に対応するASCII/Unicodeの整数値に変換されて行われます。文字 'a' のコードは 97、文字 'b' のコードは 98 です。97 + 98 の計算が行われ、結果の 195 が出力され改行されます。
System.out.println('a'); の実行文字 'a' がそのまま出力され改行されます。
56:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
import java.util.ArrayList;
public class Main {
public static void main(String[]args) {
int i[]= {2,3,1,4};int k=0;
ArrayListj=new ArrayList();
while(k<i.length) {
if(i[k]%2==1)j.add(i[k]);
else j.add(i[k]-1);
k++;
}
for(int l:j) {
if(l==i[2])j.remove(i[2]);
else System.out.print(l);
}
}
}
A:何も出力されない
B:33
C:11
D:1
E:コンパイルエラーが生じる
F:実行時に例外がスローされる
正解を確認する
このコードは、java.util.ConcurrentModificationExceptionという実行時エラーで停止します。
1. ArrayList j の作成
まず、コードの前半で配列 i の要素に基づき、ArrayList j が作成されます。配列 i は {2, 3, 1, 4} です。
偶数の場合は 1 を引いた値、奇数の場合はそのままの値が j に追加されます。結果、ArrayList j は [1, 3, 1, 3] となります。
このとき、i[2] の値は 1 です。
2. forループ
変数 l は j の最初の要素である 1 を取ります。条件式 l (1) == i[2] (1) が真となります。
j.remove(i[2])、つまり j.remove(1) が実行されます。ArrayList j は Raw Type で宣言されているため、引数 1 は インデックス として解釈されます。インデックス 1 の要素である 3 が j から削除されます。j は [1, 1, 3] に変化します。繰り上がりにより現在評価中の要素は2番目の1となります。リストの要素数が変更された直後に2回目のループに入るさいに次の要素3が読み込まれるため例外が発生します。
57:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Test {
public static void main(String[] args) {
int a = 4 + 2 * 22 / (4 + 4);
int b = a;
int c = 2;
c *= a /= 2;
System.out.println("" + a + b + c);
}
}
A:494
B:16
C:21
D:498
E:448
F:コンパイルエラーが生じる
正解を確認する
最初に変数 a と b の値が計算されます。int a = 4 + 2 * 22 / (4 + 4);
まず括弧内が計算されます。4 + 4 は 8 となります。a = 4 + 2 * 22 / 8;次に乗算と除算が左から順に実行されます。
2 * 22 は 44 となります。a = 4 + 44 / 8;次に除算が実行されます。整数演算なので、小数点以下は切り捨てられます。44 / 8 は 5.5 ですが、整数演算なので 5 となります。最後に加算が実行されます。a = 9;
次に b に a の値が代入されます。int b = a;b は 9 となります。
次に複合代入演算子を含む行が実行されます。c *= a /= 2;この行では、代入演算子の結合規則と実行順序が重要です。代入演算子(= や複合代入演算子)は右から左に結合します。まず、右側の代入演算子 a /= 2 が実行されます。これは a = a / 2 と同じ意味です。
a の現在の値は 9 なので、a = 9 / 2 となり、整数演算により a の新しい値は 4 となります。この式 a /= 2 の結果の値は、代入後の a の値、つまり 4 となります。
次に、左側の代入演算子 c *= ... が実行されます。これは c = c * (...) と同じ意味です。括弧内の値は、前のステップで計算された a /= 2 の結果の値である 4 です。c の現在の値は 2 なので、c = 2 * 4 となり、c の新しい値は 8 となります。
最後に以下の行が実行されます。System.out.println("" + a + b + c);""(空の文字列)との結合により、すべての数値が文字列に変換されて連結されます。a の値 4、b の値 9、c の値 8 がこの順に連結されます。出力される文字列は "498" となります。
58:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Test {
public static void main(String[] args) {
while (true) {
if (false) {
System.out.print(0);
} else if (true) {
System.out.print(1);
}
if (2 == 2) {
break;
}
}
}
}
A:0
B:1
C:01
D:無限ループが生じる
E:コンパイルエラー
F:実行時に例外がスローされる
正解を確認する
while (true) により、ループが開始されます。 if (false) は常に偽(false)なので、System.out.print(0); は実行されません。 else if (true) は常に真(true)なので、System.out.print(1); が実行されます。これにより、コンソールに 1 が出力されます。 次に、二つ目の if (2 == 2) が評価されます。2 == 2 は常に真(true)です。 if ブロックの中の break; が実行されます。while (true) ループを即座に中断(脱出)させます
59:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。
public class Test {
public static void main(String[] args) {
byte a = 0b111;
a(a);
}
private static void a(byte i) {
if ((i ^ 1) % 2 == 1) {
return;
}
switch(i & 1) {
case 1 -> System.out.print((double)i/3 + (i/3.0));
default -> System.out.print(i | 0b0000);
}
}
}
A:4.33333333333334
B:4.66666666666667
C:4
D:7
E:コンパイルエラーが生じる
F:実行時に例外がスローされる
正解を確認する
0b は二進数リテラルを示します。0b111 は十進数で 7 です。
メソッド a(byte i) が呼び出され、引数 i に 7 が渡されます。
if ((i ^ 1) % 2 == 1) { return; }
ビット排他的論理和(^)を計算します。7 ^ 1 は 6 となります。(二進数では 0111 ^ 0001 = 0110())剰余演算子(%)を計算します。6 % 2 は 0 です。if文の条件式は 0 == 1 となり、これは 偽(false)です。したがって、return は実行されず、処理は switch 式へ進みます。
switch(i & 1)
ビット論理積(&)を計算します。7 & 1 は 1 です。(二進数では 0111 & 0001 = 0001)switch 式の評価結果は 1 なので、case 1 の処理が実行されます。
(double)i/3: i(7)がキャストにより double型 の 7.0 に変換されます。7.0 / 3 は double型 の 2.3333333333333335 となります。
(i/3.0):3.0 が double型 であるため、i(7)は自動的に double型 の 7.0 にプロモートされます。7.0 / 3.0 は double型 の 2.3333333333333335 となります。
2.3333333333333335 + 2.3333333333333335 は double型 の 4.666666666666667 となります。(最後の桁は浮動小数点数の精度により丸められます)
60:次のコードをコンパイル、実行したときの結果として正しいものを選びなさい。るものとする。
public class Main {
static Main m;
public static void main(String[] args) {
A a = new A();
try (a; B b = new B()) {
System.out.print("try");
throw new java.io.IOException();
} catch(RuntimeException i){
System.out.print("R");
}catch(java.io.IOException j) {
System.out.print("I");
}finally {
m.close();
}
}
public void close() {}
}
class A extends Main implements java.io.Closeable {
public void close() {
m = new A();
System.out.print("A");
}
}
class B extends Main implements AutoCloseable {
public void close() {
m = new B();
throw new RuntimeException();
}
}
A:AtryIA
B:tryARA
C:tryARIA
D:tryAIA
E:コンパイルエラーが生じる
F:実行時に例外がスローされる
正解を確認する
1.try ブロックの実行
A a = new A(); の初期化そしてB b = new B() が try の括弧内で初期化されます。try ブロック本体が実行され、System.out.print("try"); が実行されます。throw new java.io.IOException(); が実行され、IOException(E1 とします)が発生します。
2.try-with-resources によるリソースのクローズ
try ブロックで例外が発生したため、catch ブロックに移る 前 に、try で宣言されたリソースが自動的にクローズされます。
クローズは、宣言されたのと 逆の順序 で実行されます。
b.close() が実行され、b の close メソッドが呼び出されます。m = new B(); が実行され、static 変数 m が B のインスタンスを参照します。throw new RuntimeException(); が実行され、RuntimeException(E2 とします)が発生します。
b のクローズで例外 E2 が発生しましたが、a のクローズも必ず 実行されます。a.close() が実行され、a の close メソッドが呼び出されます。m = new A(); が実行され、static 変数 m が A のインスタンスを参照するように上書きされます。
System.out.print("A"); が実行されます。
例外の処理(catch ブロック)
try ブロック内で発生した E1 (IOException) と、リソースクローズ中に発生した E2 (RuntimeException) の二つの例外が存在します。try-with-resources 構文では、try ブロック本体で発生した例外(E1)が「主たる例外」として扱われます。
リソースクローズ中の例外(E2)は「抑制された例外」として E1 に追加され、E1 のみが catch ブロックの対象となります。
catch(RuntimeException i): E1 は IOException なので、一致せずスキップされます。catch(java.io.IOException j): E1 と一致します。System.out.print("I"); が実行されます。
finally ブロックの実行
catch ブロックの実行後、finally ブロックが必ず実行されます。m.close(); が実行されます。この時点で m が参照しているのは、a.close() メソッド内で最後に代入された A のインスタンスです。したがって、A クラスの close メソッドが再び呼び出されます。
m = new A(); が実行されます(m は新しい A インスタンスを参照します)。System.out.print("A"); が実行されます。
コメント