はじめに
JavaBronze SE7/8 試験を受けました。
勉強をする中で個人的に気になった問題について紹介していきます。
※全問正解にはなりませんでしたが合格しました。
1問目.int型とchar型の互換性
public class Q1 {
public static void main(String[] args) {
char chr = 65;
int num = 'A';
Q1 q1 = new Q1();
q1.func1(chr, num);
}
void func1(char chr, int num) {
System.out.println("chr:" + chr);
System.out.println("num:" + num);
}
}
このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。
###A. chr:A
num:65
B. コンパイルエラーが発生する
C. 実行時エラーが発生する
D. 何も表示されない
解説
プリミティブ型であるint型とchar型には互換性があり、暗黙的なキャストが可能です。
char型変数にint型を代入すると、ASCIIコード表に対応した文字が代入されます。
逆にint型変数にchar型を代入すると、その文字に対応した10進数が代入されます。
正解:A
2問目.インクリメント演算子とデクリメント演算子
public class Q2 {
public static void main(String[] args) {
int num = 1;
System.out.print(++num + num++ + --num + num--);
}
}
このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。
A.4
B.6
C.7
D.8
解説
++numは2、num++は2、--numは2、num--は2として計算され、合計して8となります。
注意しなければいけないのは、num++が2として計算された後にインクリメントされ3となりますが、
その後すぐに--numによってデクリメントされ2になるところです。
後置されたインクリメント、デクリメント演算子は、右側に同じ変数がある場合、その変数に対して処理が反映されます。
正解:D
3問目.無限ループと実行時エラー
public class Q3 {
public static void main(String[] args) {
char[] chr = { 'A', 'B', 'C' };
while (true)
for (int i = 0; i <= chr.length; i++)
System.out.println(chr[i]);
}
}
このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。
A. 正常に実行が終了する
B. 無限ループになる
C. コンパイルエラーが発生する
D. 実行時エラーが発生する
解説
while文で無限ループ、for文で実行時エラー(ArrayIndexOutOfBoundsException)が発生します。
しかし、for文で実行時エラーが発生した時に、実行中の処理の途中であったとしても
その処理を途中でストップして実行時エラーとして出力されます。
これらが同時に発生している場合は以下の順番に起きる可能性が判断されていきます。
1.コンパイルエラー
2.実行時エラー
3.無限ループ
4.正常に実行が終了
正解:D
4問目.オーバーライドの関係性
class Super {
static void func(String str) {
}
}
class Sub extends Super {
String str;
void func(String str) {
this.str = str;
}
void print() {
System.out.println(str);
}
}
public class Q4 {
public static void main(String[] args) {
Sub sub = new Sub();
sub.func("Hello World");
sub.print();
}
}
このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。
A. Hello World
B. コンパイルエラーが発生する
C. 実行時エラーが発生する
D. 何も表示されない
解説
SuperクラスのfuncメソッドはSubクラスのfuncメソッドにオーバーライドされていますが、staticで修飾されているためコンパイルエラーとなります。
注意しなければいけないのは、この問題はmainメソッドのトレースを行ってもエラーに気が付けないところです。
mainメソッドの処理ではなく、スーパークラスとサブクラスのオーバーライドの関係性にあるメソッドに着目する必要があります。
正解:B
5問目.クラス型のキャスト
class Super {
void print() {
System.out.println("Hello World");
}
}
class Sub extends Super {
}
public class Q5 {
public static void main(String[] args) {
Sub sub;
Super spr;
sub = new Sub();
spr = sub;
sub = spr;
sub.print();
}
}
このコードをコンパイルすると、どのような結果になりますか。1つ選択してください。
A. Hello World
B. コンパイルエラーが発生する
C. 実行時エラーが発生する
D. 何も表示されない
解説
サブクラス型 → スーパークラス型 への代入を行うspr = sub;
の行ではエラーは起きず、
スーパークラス型 → サブクラス型 への代入を行うsub = spr;
の行でコンパイルエラーが起きます。
基本データ型の場合では、小さい型の変数に大きい型の変数を代入する場合、明示的なキャストが必要です。
例)
double double_01 = 100.0;
int int_01 = double_01; // 暗黙的なキャスト(エラーが起こる)
int int_01 = (int)double_01; // 明示的なキャスト(エラーが起こらない)
ではクラス型の場合、大きな型(スーパークラス+差分クラス)であるサブクラス型 → 小さい型(スーパークラスのみ)であるスーパークラスに代入してもエラーが起こらず、その逆で起こるのでしょうか?
その理由はコンパイラはコンパイルを型の互換性をチェックしてエラーか判断するためです。
基本データの場合、型の大小で互換性を判断しますが、クラス型の場合はクラスの内容を見て判断されます。
サブクラスはどのスーパークラスを継承しているか書かれていますが、スーパークラスにはどのサブクラスから継承されているか書かれていません。
そのため、サブクラスにスーパークラスを代入する場合は互換性が分かるためエラーになりませんが、スーパークラスにサブクラスを代入する場合は互換性が分からないため、明示的にキャストを行わないとコンパイルエラーになるのです。