狙え正答率7.25%アップ!! Java Silver頻出問題5つ解説！

※ 80問出題されるので1問1.25%換算。

# はじめに

先日（2023/06）、Oracle社のJava SE 11 Programmer I（Java Silver）を受験して合格しました。

Javaの知識を問う試験ですが、出題される問題の傾向としては、

1. Javaを使う人は分かっていて然るべきこと。
2. 知ってて損はないけどそこまで頭に入れておくべきかな？ということ。
3. きっとこの試験でしか使わないわということ。

体感として、この3つの割合が5:3:2くらいだったなと思います。

1については入門書などできっちり勉強するとしましょう。ここでは、勉強メモ供養として2にあたるトピックを5つピックアップして紹介します。いずれも出題される可能性は高いと思います。実際、私が受験の際にいずれも出題されました。抑えておけば5問ていど正答できる問題が増えるでしょう。


## 勉強時間や勉強方法

一応ね。

新卒で入った会社の新人研修がJavaだったので基礎的なところは習得済みでした。その後はJavaを使う機会はなかったけど、このたび転職して配属先で使うことになったので、復習がてらJava Silverを取ることにしました。入社から配属まで2週間ほど自習しててよかったのでこの時間を使いました。Java Silverのための勉強時間としては80時間くらいです。

勉強方法は、

- 新人研修でも使った「[スッキリわかるJava入門](https://sukkiri.jp/books/sukkiri_java3)」を読み直す。
- 「[独習Java](https://www.shoeisha.co.jp/book/detail/9784798151120)」に目を通す。
- 「[徹底攻略Java SE 11 Silver問題集](https://book.impress.co.jp/books/1118101186)」（通称「黒本」）を2周する。

と、たぶんまあスタンダードな感じだと思います。

出題される問題はけっこうクセがあります。「開発初心者向け資格」（[公式ページ](https://www.oracle.com/jp/education/certification/1z0-815-jpn-48261-ja.html)より） とされていますが、普段からJavaを書いてるぜ、という人でも勉強なしで受かるのは難しいと思います。試験対策として最も有効なのは黒本でしょう。黒本の問題の類題（なんならまるっと同じ問題）が出題されます。地力がある人も黒本をやって損はない……というか必須だと思います。


# 5選！ 割と細かいJava知識！

以下5つです:

1. フィールドとローカル変数の名前が被ったときの挙動
2. varが使えるとき、使えないとき
3. 配列の宣言と初期化のいろいろ
4. アクセス修飾子を通して整理する継承やオーバーライド
5. switch命令のフォールスルー

## フィールドとローカル変数の名前が被ったときの挙動

まずおさらい。クラスの直下にある変数をフィールドと呼び、メソッドの中の変数をローカル変数と呼ぶのでした。以下のItemクラスであれば、`number`がフィールドで、`num`がローカル変数です。

```Java
public class Item {
	public int number;
	public void setNumber(int num) {
		this.number = num;
	}
}
```

`Item`をnewして`setNumber(10)`を呼び出せば、フィールドの`number`に10が入ります。

では、`this.number`の`this.`を外したら？

```Java
public class Item {
	public int number;
	public void setNumber(int num) {
		number = num;
	}
}
```

これは大丈夫。フィールド変数であることを明示するために`this.number = num`と書く**べき**ですが、`setNumber(10)`を呼び出せば`number`に10が入ります。

続いて、`this.number`のまま、`num`を`number`に変えたら？

```Java
public class Item {
	public int number;
	public void setNumber(int number) {
		this.number = number;
	}
}
```

これも大丈夫。フィールドの名前とローカル変数の名前が被っているけど、フィールドを明示する`this.`がついているので、`setNumber(10)`を呼び出せばフィールドの`number`に10が入ります。ただし、フィールドの名前とローカル変数の名前が被るのは紛らわしいので**作法としてはよくない** （この例はセッターなので例外として）。

では、`this.`まで外してしまったら？

```Java
public class Item {
	public int number;
	public void setNumber(int number) {
		number = number;
	}
}
```

こうなると、`setNumber()` の中の`number`は左辺も右辺もローカル変数です。`setNumber(10)`としてもフィールドの`number`が書き換わることはありません。しかし、コンパイルエラーになるわけでもない。

実際にJava Silverで出題される問題としてはこんな感じ [^1] [^2]:

----

```
public class Item {
	public int number;
	public void setNumber(int number) {
		number = number;
	}
}
```

というクラスがあるとき、

```
public class Main {
    public static void main(String[] args) {
    	Item item = new Item();
    	item.setNumber(10);
    	System.out.println(item.number);
    }
}
```

mainメソッドを実行したときの正しい結果を次の中から選びなさい。

1. 0が出力される。
2. 10が出力される。
3. 予期せぬ値が出力される。
4. コンパイルエラーとなる。
5. 実行時エラーとなる。

正解: 1

----

実際に開発するときは、
1. フィールドとローカル変数に同じ名前をなるべく使うな。
2. フィールドを参照するときは`this.`を使え。

この2つを守れというのがそもそもの話ですが、こういう問題が出ます。

[^1]: ちなみにこの問題は、フィールドを初期化していない場合は規定値（数値の基本型なら0とか0.0, booleanならfalse, 参照型ならnull）が入る、という知識も問われています。なので答えは「0が出力される」の1です。とはいえ、「フィールドの規定値がその型の規定値そのままである場合には、値を初期化しなくてもかまいません。ただし、規定値に頼ったコードは、可読性の観点からは好ましくありません」（独習Java p280）。

[^2]: 試験ではコードに色は付いてないからシンタックスハイライト消した。

## varが使えるとき、使えないとき

これは知っておいた方がいい寄りの知識かもしれない。

Javaでは、ローカル変数を宣言するときには[^3]、左辺の変数の型を`var`と書いてもいいというルールがあります。たとえば、
```Java
ArrayList<String> list = new ArrayList<String>();
```
これを、
```Java
var list = new ArrayList<String>();
```
と書いてもOKです。
varを使っていいケースははっきりしています。**右辺の情報だけで型が明確に定まる場合のみ**です。

[^3]: ローカル変数だけです。フィールドの宣言に`var`は使えません。これも出題されます。


実際の問題としてはこんな感じで出題されます[^4]。

----

次の変数宣言のうち、コンパイルエラーとならないものを選びなさい（3つ選択）。

1. var a;
2. var b = null;
3. var c = "abc";
4. var d = new ArrayList<>();
5. for (var e = 0;;) {}

正解: 3,4,5

----

[^4]: `ArrayList<>` でジェネリクスに型を指定しないと自動的にObject型になります。なので4もエラーにはなりません。これも出題されます。

varを使う是非は色んな議論があるようですがそのあたりは見ないことにします。僕はあまり使わない派です。


## 配列の宣言と初期化のいろいろ

配列の宣言と初期化のやりかたはいくつかあるのでまとめます。

**やりかた1** ... 宣言のみ
```
データ型[] 配列名 = new データ型[要素数];
```
例:
```Java
int[] list = new int[5];
```
メモ: 宣言だけして初期化しなかった場合、要素は規定値を持つ。数値の基本型なら0とか0.0、booleanならfalse、参照型ならnull。上の例だと`list`は`[0,0,0,0,0]`となる。

**やりかた2** ... 宣言と初期化
```
データ型[] 配列名 = new データ型[]{要素1, 要素2, ... }
```
例:
```Java
int[] list = new int[]{0, 1, 2, 3, 4};
```
メモ: 右辺の`[]`は省略不可で、また、`[]`の中に要素数を書いてもダメ。

**やりかた3**... 宣言と初期化
```
データ型[] 配列名 = {要素1, 要素2, ... };
```
例:
```Java
int[] list = {0, 1, 2, 3, 4};
```

宣言と初期化にまつわるTips:
- やりかた1とやりかた2については、左辺の`データ型[]`を`var`で置き換えてもいい。ただし、やりかた3では`var`は使えない。
- やりかた2とやりかた3について、要素はカンマで終わってもいい。つまり、`{0,1,2,3,4,}`はOK。
- やりかた1～やりかた3まで、いずれも、`データ型[] 配列名` の代わりに`データ型 配列名[]` と書いても可。つまり、`int list[]`と書いてもエラーにならない。
  - これはC言語の名残として残されている。ただし、「配列である」ことも型の情報だから`データ型[]`とする方が素直（独習Java p76）。

その他の配列にまつわるTips:
- 要素数0の配列も可。nullになるわけではない。ただし、配列は要素の追加ができないから無意味。
- 配列自体は参照型。なので`int[] list = null;` は入る。
- `配列名.length`で要素数が取れる。メソッドじゃないよ。

だいたい上記を踏まえて、この書き方はコンパイル可能か？ みたいな問題が出ます。


## アクセス修飾子を通して整理する継承やオーバーライド

アクセス修飾子は暗記せねば戦えません。覚えよう。
上ほどゆるい。

| アクセス修飾子 | 効能 |
|:-----------|:------------|
| public | すべてのクラスからアクセスできる |
| protected | 同じパッケージのクラス、および子クラスからアクセスできる（子クラスが別パッケージでも可） |
| 修飾なし | 同じパッケージのクラスからアクセスできる。パッケージプライベート |
| private | 同じクラスからのみアクセスできる |

ざざざっと箇条書きにします。覚えよう。

- インターフェイスはpublicのみ（なので省略可）。
  - インターフェイスのメソッドと定数フィールドもpublicのみ（なので省略可）。
- クラスはpublicまたはパッケージプライベートのみ。
  - メンバー（フィールドとメソッド）はいずれも指定可。
  - ローカル変数にはアクセス修飾子は付けることはできない。クラスの外から触る手段はないから当然ではある。
- 継承や実装したときのクラスのアクセス修飾子は、元のクラスやインターフェイスのアクセス修飾子と同じか、よりゆるいものでなければならない。
- メソッドをオーバーライドしたときのアクセス修飾子も、元のメソッドのアクセス修飾子と同じか、よりゆるいものでなければならない。

パッケージプライベートのクラスを継承したクラスで許されるアクセス修飾子は次のうちどれ？ みたいな問題が出ます。「同じか、よりゆるく」と覚えておけばだいたい即答できます。


## switch命令のフォールスルー

普通のswitchの使い方はこんな感じ:

```Java
String rank = "A";
switch (rank) {
    case "S":
        System.out.println("めっちゃよい");
        break;
    case "A":
        System.out.println("とてもよい");
        break;
    case "B":
        System.out.println("よい");
        break;
    default:
        System.out.println("ふつう");
        break;
}
```
実行結果:
```Java
とてもよい
```

case句ごとに`break;`をつけないといけませんでした。
ただし、つけなくてもコンパイルエラーになるわけではありません。上の例で`break;`を外すと、

```Java
String rank = "A";
switch (rank) {
    case "S":
        System.out.println("めっちゃよい");
    case "A":
        System.out.println("とてもよい");
    case "B":
        System.out.println("よい");
    default:
        System.out.println("ふつう");
}
```

実行結果:
```Java
とてもよい
よい
ふつう
```

と、条件に合致したcaseに移動したのち、後続のcase句/default句も実行されてしまいます。

この挙動にはフォールスルーというしゃれた名前がついています。名前がついているものの、フォールスルーは分かりにくいだけなので、以下のようなケース以外では使うべきではありません（独習Java p122）。

```Java
var drink = "ビール";
switch (drink) {
    case "日本酒":
    case "ビール":
    case "ワイン":
        System.out.println("醸造酒です。");
        break;
    case "ブランデー":
    case "ウイスキー":
        System.out.println("蒸留酒です。");
        break;
```

Java Silverでは、こんな無意味なコードやめてよ！という感じのフォールスルーが出題されます。げっそりするので紹介はしません。ぜひ君の目で確かめてみてくれ！


# おわりに

Java Silverの出題範囲内の割と細かい知識を5つ紹介しました。
IDEを使って書いていれば警告なりエラーなり出してくれるから、そこまで頭に入れてなくてもいいんじゃないかな……？ 覚えておかないとだめ……？ みたいな問題がどしどし出題されます。

しかし、文句ばかり書いてきたけど、勉強した結果、Javaに対する理解が深まったのは確かです。こういう根拠でこう！ という気持ちでコードを読み書きできることが増えたので無駄だったとは思いません。もうちょいなんとかならんのかとは思います。