Java SE 8 Programmer I (1Z0-808-JPN) サンプル問題

問題1

次のコードを確認してください。

public class Test {
    private int score = 300;
    private int point = 0;

    private void applyScore(int sc) {
        int point = sc;
        score = point;
    }

    public void printResult() {
        int score = point;
        System.out.println(score + " : " + point);
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.applyScore(500);
        t.printResult();
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. コンパイルに失敗する。
  2. 0 : 0
  3. 500 : 500
  4. 300 : 0

解答: 2

解説:

インスタンスのフィールド変数と、メソッド内で宣言されたローカル変数の違いについて問う問題です。
フィールド変数名とローカル変数名が重複していることでのコンパイル・エラーは起こらないので、選択肢1は当てはまりません。
t.applyScore(500); を実行したとき、applyScoreメソッドの中ではローカル変数としてpointというint型の変数を新たに宣言し、引数として与えられた500を格納しています。 ここで、ローカル変数として宣言されたpointとフィールド変数のpointは異なるものとなるので、フィールド変数のpointは0のままです。
また、applyScoreメソッドの2行目で、フィールド変数のscoreにローカル変数pointの値(500)を代入しているため、このメソッドが終わった時点でこのインスタンスのフィールド変数scoreは500になります。
次に、printResultメソッドでローカル変数scoreを宣言し、フィールド変数pointの値(0)を代入しています。
printResultメソッドでは、このローカル変数scoreの値(0)とフィールド変数pointの値(0)を出力しているため、解答のような表示になります。


問題2

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        String str = args[0];
        String message = str + args[3];
        System.out.println(message);
    }
}

このコードを実行して、Jaと表示させるためには、コンパイルと実行のコマンドをどのように組み合わせればよいですか。

  1. javac Test.java
    java Test Java

  2. javac Test.java
    java Test J a v a

  3. javac Test
    java Test.class [Java]

  4. javac Test.java -args J a v a
    java Test

  5. javac Test.java
    java Test({“J”, “a”, “v”, “a”})

解答:2

解説:

Javaプログラムのコンパイルのコマンドと実行のコマンドを問う問題です。
コンパイルは、javacコマンドを使用し、拡張子まで含めたファイル名を指定します。
実行は、javaコマンドを使用し、クラスの名前(拡張子は不要)のあとにコマンドライン引数のリスト(mainメソッドの引数、String型配列argsに渡される)をスペース区切りで指定します。

問題3

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        int iNum = 5;
        float fNum = 3.4f;
        double dNum = 7.9;

        if (iNum > 5) {
            iNum = fNum;    // line 1
        } else {
            fNum = dNum;    // line 2
        }
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. line 1でコンパイル・エラーが発生する。
  2. line 2でコンパイル・エラーが発生する。
  3. line 1およびline 2でコンパイル・エラーが発生する。
  4. 正常にコンパイルおよび実行される。

解答:3

解説:

数値のプリミティブ型の大きさは、double > float > long > int > short > byte の順です。
大きな型の変数の値を、より小さい型の変数に代入しようとすると、コンパイルエラーが発生します。(型変換が必要)
また、ソースコードの処理を追うと、if文が必ずfalseとなるため、line 1は実行されませんがコンパイラでは判断できないので、line 1についてもコンパイル・エラーとなります。 なお、デフォルトで整数値のリテラルはint型、小数値のリテラルはdouble型として扱われますが、それぞれリテラルの末尾にL(またはl)やF(またはf) を付けることでlong型やfloat型として扱うことができます。


問題4

次のコードを確認してください。

public class Test {
    int amount;
    String name = "Java";

    public static void main(String[] args) {
        Test test = new Test();
        test.amount = 10;
        test.update(test.amount);
        test.modify(test);
        System.out.println(test.name + " " + test.amount);
    }

    private void update(int n) {
        n = n * n;
    }

    private void modify(Test t) {
        t.amount = t.amount * t.amount;
        name.concat("SE8");
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. Java SE8 100
  2. Java SE8 10000
  3. Java 100
  4. Java 10000

解答:3

解説:

値渡しと参照渡しの違い、およびString型の不変性について問う問題です。
test.update(test.amount); を実行すると、testオブジェクトではなく10という値そのものを引数として渡しているため、updateメソッドで値を書き換えたとしてもtestオブジェクトには影響はありません。
一方、test.modify(test); では、testオブジェクトの参照を引数として渡しているので、計算した結果は呼出し元のtestに反映されています。
また、String型のnameについて、concatメソッドで文字列を連結しているように見えますが、Stringクラスは不変(状態、つまりインスタンスのフィールド変数の値が変わらないもの)であるため、初期値の"Java"から変化しません。
Stringクラスの文字列変更メソッドについては、変更された新しい文字列が「戻り値として」戻ってくるという点に注意が必要です。

問題5

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        String str = "1";

        switch (str) {
            case "1":
                System.out.print(str + 3 + 4);
            case "2":
                System.out.print(str + (3 + 4));
        }
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. 8
  2. 134
  3. 17
  4. 13417
  5. 1347
  6. 88
  7. コンパイル・エラーが発生する。

解答:4

解説:

switch文と文字列の連結演算に関する問題です。
Java SE7からswitch文にString型も使用することができるようになったため、コンパイル・エラーにはなりません。
また、switch文は該当するcaseの行に処理が飛んだ後、break; がないとそのまま下に実行されていくため、2つのprintメソッドが順に実行されることになります。
最初のprintメソッドでは、(str + 3 + 4) とあるので、文字列として “1” と “3” を連結し、"13" になったあと、さらに “4” が連結され、最終的に “134” となります。
また、このとき、str自体は不変なので “1” のままです。
2つめのprintメソッドでは、先に(3 + 4)が数値の演算として 7 になるため、"1" と “7” を連結して “17” が出力されます。
printメソッドは改行コードを出力しないため、"134" と “17” が続けて出力されることになり、"13417" という文字列が画面(標準出力)に表示されます。

問題6

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        String msg1 = "Good";
        String msg2 = msg1;    // line 1
        String msg3 = new String("Good");
        String[] msgArr = {"G", "o", "o", "d"};
        String msg4 = "";
        for (String msg : msgArr) {
            msg4 += msg;
        }
        System.out.println(msg1 == msg2);    // line 2
        System.out.println(msg1 == msg3);    // line 3
        System.out.println(msg1.equals(msg4));    // line 4
    }
}

trueが3回出力されるようにするには、どのように変更すればよいですか。

  1. line 1を以下のように変更する。
    String msg2 = new String(msg1);
  2. line 2を以下のように変更する。
    System.out.println(msg1.equals(msg2));
  3. line 3を以下のように変更する。
    System.out.println(msg1.equals(msg3));
  4. line 4を以下のように変更する。
    System.out.println(msg1 == msg4);
  5. 変更の必要はない。

解答:3

解説:

==演算子による比較とequalsメソッドによる比較の違いを問う問題です。
==演算子による比較では、参照型変数同士が同じオブジェクトを参照しているときのみtrueになります。(つまり、line 2はtrue)
それに対し、equalsメソッドを使用した場合は、文字列自体が等しいかどうかの比較になります。(つまり、line 4はtrue)
new演算子で生成したStringオブジェクト(msg3)や連結して生成したStringオブジェクト(msg4)は、明示的に新しいオブジェクトを生成していることになりますので、msg1と==演算子で比較してもfalseになってしまいます。(つまり、line 3はfalse)
したがって、line 3の部分をequalsメソッドに変えることでtrueと出力させることができます。

問題7

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        int num = 5;
        int count = num++;
        int result = ++count;

        String str = num == count ? num < result ? "ARG" : "BOL" : "CHL";
        System.out.println(str);
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. ARG
  2. BOL
  3. CHL
  4. コンパイル・エラーが発生する。

解答:2

解説:

インクリメント演算子と三項演算子に関する問題です。
int count = num++; では、後置インクリメントなので、先にnumの値(5)をcountに代入してから、インクリメントしています。(この時点で、countは5、numは6)
int result = ++count; では、前置インクリメントなので、先にcountの値(5)をインクリメントしてから、resultに代入しています。(この時点で、result, count, numすべて6)
三項演算子は、?の前の条件式がtrueのときは : の左、falseのときは : の右が評価されるため、num == count(結果はtrue)については、num < result ? “ARG” : “BOL” の部分が評価されることになります。
num < result ? “ARG” : “BOL” については、num < resultがfalseとなるため、"BOL" が変数strに代入されて出力されていくことになります。

問題8

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        char[][] chArr = new char[2][];
        chArr[1] = new char[]{'X', 'O', 'O', 'X'};
        char[] ox = {'O' , 'X'};
        chArr[0] = ox;

        for (char[] ca : chArr) {
            for (int i=ca.length - 1; i>=0; i--) {
                System.out.print(ca[i]);
            }
            System.out.println();
        }
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. XO
    XOOX
  2. OX
    XOOX
  3. XOOX
    OX
  4. XOOX
    XO
  5. コンパイル・エラーが発生する。
  6. 実行時に例外がスローされる。

解答:1

解説:

2次元配列および、forループに関する問題です。
2次元配列は、配列の中の要素がさらに配列になっている「入れ子」のような構造になっています。
chArr[0] = ox; の部分までで、以下のような配列が生成されています。

  • 1つめ(添字0)の要素:{‘O’ , ‘X’}
  • 2つめ(添字1)の要素:{‘X’, ‘O’, ‘O’, ‘X’}

これを、for (char[] ca : chArr)の拡張forループで取り出しつつループしています。
拡張forループでは、要素として上記の配列が変数caに取り出されていますが、これをさらに標準forループで処理しています。
標準forループでは、caに取り出された配列の長さ - 1(つまり最後の添字)から0(つまり最初の添字)までデクリメントされながら出力されており、配列の逆順でアクセスしていることになります。
拡張forループでは、要素を順に取り出していくことしかできないのに対して、標準forループではこうした逆順アクセスや、要素を1つ飛ばしでアクセスするなど柔軟なループ処理をすることができます。

問題9

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};

        for (int num : arr) {               // line 1
            System.out.print(num + " ");    // line 2
            // line 3
        }
    }
}

このコードを実行して、以下のように表示するためにはどのように変更すればよいですか。

 2 4 6 8 
  1. line 1を以下のように変更する。
    for (int num = num + 1 : arr) {
  2. line 2を以下のように変更する。
    System.out.print(num++ + " ");
  3. line 2を以下のように変更する。
    System.out.print(++num + " ");
  4. line 3に以下のコードを追加する。
    continue;
  5. どれも当てはまらない。

解答:5

解説:

forループの性質に関する問題です。
拡張forループでは、: の左辺は要素を取り出して格納するための変数を宣言する部分なので、1のような書き方はコンパイル・エラーになります。
また、選択肢2は出力されたあとにnumがインクリメントされるだけなので、1〜9が出力されます。
選択肢3についても、出力される前にnumをインクリメントしているだけで、2〜10が出力されてしまいます。
選択肢4のcontinueについても、処理に何も影響をおよぼさないため、設問のような出力にすることはできません。
結果として、5が正解ということになります。
なお、設問のような出力にするためには、標準ループを使用して

for (int i=1; i<arr.length; i+=2) {
    System.out.print(arr[i] + " ");
}

と1つ飛ばしにアクセスするか、もしくは奇数部分をcontinueで飛ばすようにして、

if ((num % 2) != 0) { continue; }

のようにする必要があります。

問題10

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        String root = "";
        String[] names = {"MAD", "DXB", "NRT"};

        for (String ap : names) {
            switch (ap) {
                case "MAD":
                    root += "1";
                case "DXB":
                    root += "2";
                    break;
                case "NRT":
                    root += "3";
            }
            break;
            root += "4";
        }
        System.out.println("root : " + root);
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. 1
  2. 12
  3. 1223
  4. コンパイル・エラーが発生する。

解答:4

解説:

switch文のbreakとループ文のbreakに関する問題です。
switch文の場合は、評価する値に一致するcase部分に処理が飛んだあと、breakをもってswitchから出るため、break後に実行されない処理があっても構いません。
それに対して、ループ文の中に出現するbreakの場合は、そこでループ処理が必ず終了するため、break後に書いた処理は絶対に実行されないものとなり、コンパイル・エラーになります。

問題11

次のコードを確認してください。

class Product {
    static int maxAmount;
    int amount;

    public Product(int amount) {
        this.amount = amount;
    }

    public static void storeMax() {
        if (amount > maxAmount) {
            maxAmount = amount;
        }
    }

    public void printDetail() {
        System.out.println("amt : " + amount + " maxAmt : " + maxAmount);
    }
}

public class Test {
    public static void main(String[] args) {
        Product p1 = new Product(120);
        Product p2 = new Product(100);

        p1.storeMax();
        p2.storeMax();

        p1.printDetail();
        p2.printDetail();
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. amt : 120 maxAmt : 120
    amt : 100 maxAmt : 120
  2. amt : 120 maxAmt : 120
    amt : 100 maxAmt : 100
  3. amt : 120 maxAmt : 120
    amt : 100 maxAmt : 0
  4. コンパイル・エラーが発生する。
  5. 実行時に例外がスローされる。

解答:4

解説:

インスタンス・メンバとクラス・メンバ(staticキーワードが付いているもの)に関する問題です。
インスタンスからクラス・メンバにアクセスすることはできますが、その逆はできません。
ここでは、storeMaxメソッド(クラス・メソッド)からamount(インスンタンス変数)にアクセスしているため、コンパイル・エラーとなります。

問題12

次のコードを確認してください。

class Student {
    String name;
    int score;

    // line 1
    public Student(String name) {
        this();        // line 2
        this.name = name;
    }

    public Student(String name, int score) {
        // line 3
        this.score = score;    // line 4
        this(name);            // line 5
    }
}

public class Test {
    public static void main(String[] args) {
        Student std1 = new Student("Manuel");
        Student std2 = new Student("Rodrigo", 92);    // line 6
    }
}

このコードのコンパイルを成功させるために、必要な修正はどれですか。(2つ選択してください)

  1. line 1に以下のコードを追加する。
    public Student() { score = 50; }
  2. line 2を以下のように変更する。
    this(50);
  3. line 3に以下のコードを追加する。
    this();
  4. line 4とline 5を入れ替える。
  5. line 6を削除する。

解答:1 と 4

解説:

コンストラクタに関する問題です。
コンストラクタについては、以下の2つのルールを押さえるようにしてください。

  • コンストラクタが用意されていないクラスには、デフォルト・コンストラクタ(引数なしのコンストラクタ)が用意されるが、明示的にコンストラクタが用意されたクラスにはデフォルト・コンストラクタは用意されない
  • 同クラス内のコンストラクタを呼出すには this(); または this(引数リスト); と記述するが、必ずコンストラクタ内の先頭で1回のみとする
    ※ちなみにコンストラクタ呼び出し同士がお互いを呼び出していて、循環してしまうような呼び出し方もできません

設問のStudentクラスでは、line 2で呼び出しているデフォルト・コンストラクタが存在しないので、line 1の部分でに明示的に定義する必要があります。
また、line 5については、コンストラクタの先頭で呼出す必要があるのでline 4と入れ替えることで、コンパイル・エラーを解消することができます。

問題13

次のコードを確認してください。

[Customer.java] package global; public class Customer { int id; // line 1
private String name; protected int accNo;

    public void setName(String name) {
        this.name = name;
    }

    void printMessage() {  // line 2
        System.out.println("Hello " + name);
    }
}

[WebCustomer.java] package global;

public class WebCustomer extends Customer {
    private void printMessage() {  // line 3
        System.out.println("Hola " + id + " " + accNo);  // line 4
    }
}

[Test.java] package domectic; // line 5

import global.*;  // line 6

public class Test {
    public static void main(String[] args) {
        Customer cust = new WebCustomer();
        cust.id = 152;  // line 7
        cust.setName("Rocio");
    }
}

このコードのコンパイルを成功させるために、必要な修正はどれですか。(2つ選択してください)

  1. line 1を以下のように変更する。
    protected int id;
  2. line 2を以下のように変更する。
    public void printMessage() {
  3. line 3を以下のように変更する。
    public void printMessage() {
  4. line 4を以下のように変更する。
    System.out.println("Hola " + accNo);
  5. line 5とline 6を入れ替える。
  6. line 7を削除する。

解答:3 と 6

解説:

アクセス修飾子の詳細なルールについての問題です。
まず、line 1のインスタンス変数idは、パッケージ・プライベート(アクセス修飾子なし)となるため、別パッケージであるline 7のようなかたちでアクセスすることはできません。
選択肢1の修正では、この制約を解消することはできませんので、選択肢6のようにline 7を削除するしかありません。
また、line 2とline 3のようにオーバーライドの関係にあるメソッドでは、オーバーライドする側で元のメソッドよりも緩いアクセス修飾子にする必要があります。
アクセス修飾子は、public > protected > パッケージ・プライベート(アクセス修飾子なし) > privateのように厳しくなるため、line 2よりもline 3の方が緩くなる必要があります。
選択肢2では解決しませんので、選択肢3のようにオーバーライドする側のアクセス修飾子を緩めることでコンパイル・エラーを解消できます。
また、選択肢4では、親クラスの変数idに対してアクセスしないようにしていますが、同パッケージ内のクラスですので、特に必要のない修正です。
選択肢5は、package宣言がimport宣言よりも先に来ることはできませんので、誤りとなります。

問題14

次のコードを確認してください。

interface Exchangeable {
    Product exchangeProduct();
}

class Product implements Exchangeable {
    String name;
    int price;

    Product exchangeProduct() {
        return new Product();
    }
}

class Lamp extends Product {
    public Product exchangeProduct() {
        return new Lamp();
    }
}

public class Test {
    public static void main(String[] args) {
        Lamp clinker = new Lamp();
        Product replacement = clinker.exchangeProduct();
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. Productクラスでコンパイル・エラーが発生する。
  2. Lampクラスでコンパイル・エラーが発生する。
  3. Testクラスでコンパイル・エラーが発生する。
  4. 実行時例外がスローされる。
  5. 正常にコンパイルおよび実行される。

解答:1

解説:

インタフェースのデフォルトの修飾子に関する問題です。
インタフェースでは、メソッド(※)は暗黙的にはpublic abstractが付加され、変数(定数)には暗黙的にpublic static finalが付加されます。
オーバーライドする側のメソッドでは、アクセス修飾子を厳しくする方向での変更は許されないため、ProductクラスのexchangeProductメソッドでコンパイル・エラーが発生します。
※defaultメソッドとstaticメソッド以外

問題15

次のコードを確認してください。

class Player {
    String name;
    int annSal;

    public Player(String name, int annSal) {
        this.name = name;
        this.annSal = annSal;
    }
}

class Footballer extends Player {
    boolean isCap;

    public Footballer(boolean isCap) {
        // line 1
    }

    public Footballer(String name, int annSal, boolean isCap) {
        this.isCap = isCap;     // line 2
        this.name = name;       // line 3
        this.annSal = annSal;   // line 4
    }
}

public class Test {
    public static void main(String[] args) {
        Player ft1 = new Footballer(true);
        Player ft2 = new Footballer("Jorge", 20, false);  // line 5
    }
}

このコードのコンパイルを成功させるために、必要な修正はどれですか。(2つ選択してください)

  1. line 1を以下のように変更する。
    this.isCap = isCap; super("Legend", 300);
  2. line 1を以下のように変更する。
    this("Legend", 300, isCap);
  3. line 2を以下のように変更する。
    this(isCap);
  4. line 2, line 3およびline 4を以下のように変更する。
    super(name, annSal); this(isCap);
  5. line 2, line 3およびline 4を以下のように変更する。
    super(name, annSal); this.isCap = isCap;
  6. line 5を削除する。

解答:2 と 5

解説:

継承とコンストラクタ呼び出しルールに関する問題です。
継承の場合も、以下のコンストラクタに関するルールを正確に整理して理解しておく必要があります。
まず、

  • コンストラクタが用意されていないクラスには、デフォルト・コンストラクタ(引数なしのコンストラクタ)が用意されるが、明示的にコンストラクタが用意されたクラスにはデフォルト・コンストラクタは用意されない

というルールから、Playerクラスにはデフォルト・コンストラクタ(引数なしのコンストラクタ)が存在しないことになります。
また、

  • コンストラクタ内で、別コンストラクタを呼び出していない場合には、暗黙的に super(); が追加される

というルールから、Footballerクラスのコンストラクタについて、このままでは親クラスに存在しない super(); を呼び出してしまうこととなりコンパイル・エラーとなります。
したがって、Footballerクラスのコンストラクタから別コンストラクタを明示的に呼び出す必要があります。
また、選択肢に目をやると、

  • super(); や super(引数リスト); あるいは this(); や this(引数リスト); は、それぞれコンストラクタ内の冒頭で1回しか記述できない

というルールから、選択肢1と4は、super(); や this(); が冒頭にない、もしくは複数回呼び出されているためにあり得ないことが分かります。
また、選択肢6にあるように、コンストラクタの呼び出し(つまりインスタンス化)をしなかったとしてもFootballerクラス自体のコンパイル・エラーには関係がないので、この選択肢も誤りとなります。
残った選択肢2、3および5のうち、正しくコンパイルするためには選択肢2と5の組み合わせが必要となります。

問題16

例外の処理に関して正しい記述はどれですか(3つ選択してください)。

  1. Exceptionクラスのすべてのサブクラスはチェック例外である。
  2. RuntimeExceptionクラスのすべてのサブクラスはチェック例外である。
  3. Exceptionクラスのすべてのサブクラスは復旧可能である。
  4. Errorクラスのすべてのサブクラスはキャッチすべきではない重大な問題を示す。
  5. 非チェック例外は再スローできない。
  6. catchブロック内のパラメータはすべてThrowable型として扱うことができる。

解答:3 と 4 と 6

解説:

例外の種類についての知識を問う問題です。
Exceptionクラスのサブクラスうち、RuntimeExceptionクラスのサブクラスは非チェック例外となるため、選択肢1と2は誤りです。
また、選択肢5のような制約は特にないので、この選択肢も誤りとなります。
残った選択肢3、4および6については、いずれも正しい記述となります。

問題17

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        t.showData();  // line 1
    }

    private void showData() { // line 2
        openFile();
        readFile();  // line 3
    }

    private void openFile() throws IOException { // line 4
        throw new IOException();
    }

    private void readFile() {
        System.out.println("Hello");  // line 5
    }
}

このコードのコンパイルを成功させるために、必要な修正はどれですか。(2つ選択してください)

  1. line 1を以下のように変更する。
    try { t.showData(); } catch (IOException e) { }
  2. line 2を以下のように変更する。
    private void showData() throws IOException {
  3. line 3を以下のように変更する。
    try { readFile(); } catch (IOException e) { }
  4. line 4を以下のように変更する。
    private void openFile() {
  5. line 5を以下のように変更する。
    try { System.out.println("Hello"); } catch (IOException e) { }

解答:1 と 2

解説:

openFileメソッドの中で明示的に発生させているIOExceptionは、チェック例外となります。
チェック例外が発生する可能性がある部分については、コンパイルに成功するために以下のいずれかをする必要があります。

  • メソッドのシグネチャにthrows宣言を追加する
  • チェック例外が発生する箇所をtry-catch句で囲む

これらを満たす選択肢の組み合わせとして、選択肢1と2が正解となります。

問題18

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        String message = " Java ";
        message = message.concat("SE8");
        message.trim();
        System.out.println(message.indexOf(" "));
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. 0
  2. 1
  3. 4
  4. 5
  5. -1

解答:1

解説:

Stringクラスの不変性と文字列操作メソッドに関する問題です。
Stringオブジェクトは、不変(イミュータブル)なため、文字列を変更するメソッドを呼び出した場合は変更された新たな文字列が戻り値として返ってくることになります。(つまり、元の文字列オブジェクトは変更されない)
そのため、concatメソッドを呼び出して元の変数に格納している部分では “ Java SE8” (先頭は半角スペース)という文字列になっています。
次のtrimメソッドは、先頭と末尾の空白を除去するメソッドですが、除去された文字列は戻り値として返ってくることになり、変数messageに格納されている文字列は元のままとなります。(先頭に空白を含んだまま)
indexOfメソッドでは、引数として指定された文字列が最初に出現する位置のインデックスを返すため、0が出力されます。
ちなみに、indexOfメソッドで引数として指定された文字列が元の文字列に存在しない場合は、-1が返ってきます。

問題19

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        LocalDateTime ldt1 = LocalDateTime.of(2016, 7, 15, 21, 3);
        ldt1.plusMonths(2);
        ldt1.minusDays(-16);
        System.out.println(ldt1.format(DateTimeFormatter.ISO_DATE));

        LocalDateTime ldt2 = LocalDateTime.of(2016, 9, 31, 21, 3);
        ldt2.minusMonths(2);
        System.out.println(ldt2.format(DateTimeFormatter.ISO_DATE));
    }
}

このコードをコンパイルおよび実行すると、どのような結果になりますか。

  1. 何も出力されず、実行時例外がスローされる。
  2. 2016-09-31 と出力されたのち、実行時例外がスローされる。
  3. 2016-10-01 と出力されたのち、実行時例外がスローされる。
  4. 2016-07-15 と出力されたのち、実行時例外がスローされる。
  5. 2016-10-01
    2016-07-31 と出力される。
  6. 2016-10-01
    2016-08-01 と出力される。
  7. 2016-07-15
    2016-10-01 と出力される。

解答:4

解説:

Java SE 8から導入された日時APIに関する問題です。
LocalDateTimeクラスは日時を表すクラスで、ofメソッドに年月日や時分秒などの値を引数として渡して生成することができます。
このクラスもStringクラスなどと同様に不変オブジェクトとなるため、加減算のメソッド(plusXxx, minusXxx)を呼び出した場合には、元のオブジェクトの値は変わらずに新しい日時のオブジェクトが戻り値として返されます。
また、LocalDateTimeクラスは時間や分までを含んでいますが、DateTimeFormatterの指定で取り出す際に日付部分だけを抽出するように指定することも可能です。
(逆にLocalDateクラスについて、時分の部分を取り出すことはできない(実行時例外が発生する)ので注意してください)
ISOのフォーマットに基づいて文字列に変換した場合、どのような出力になるのかも確認しておくようにしてください。
さらに、LocalDateTimeオブジェクトを生成する際に、ありえない日付を生成しようとするとDateTimeExceptionが発生します。

問題20

次のコードを確認してください。

public class Test {
    public static void main(String[] args) {
        String[] cityArr = { "Tokyo", "Madrid", "Buenos Aires" };
        List<String> cities = new ArrayList<>(Arrays.asList(cityArr));

        Predicate<String> pred = 
                // line 1
        cities.removeIf(pred);

        for (String city : cities) {
            System.out.println(city);
        }
    }
}

このコードをコンパイル、および実行して

 Madrid
 Buenos Aires

と表示するために、line 1に記述することができるコードとして正しいものはどれですか。(2つ選択してください)

  1. String s -> s.startsWith("T");
  2. s -> s.startsWith("T");
  3. s -> return s.startsWith("T");
  4. (String s) -> { s.startsWith("T") };
  5. s -> { return s.startsWith("T"); };

解答:2 と 5

解説:

ラムダ式の記法に関する問題です。
Java SE8から導入されたラムダ式については、正しい書き方と省略記法について押さえておくようにしてください。
設問にあるPredicate型は、引数(ジェネリクスで指定した型)をひとつ取って、boolean値を返す関数型インタフェースです。
Predicateに指定できるラムダ式としては

(型 引数名) -> { return booleanを返す式; }

が完全な記述になりますが、->演算子の左辺、右辺ともに省略記法が可能です。
左辺については、引数がひとつのみなので、型と( )を省略することができます。
右辺については、式がひとつのみの場合、returnと;および{ }を省略することができます。
左辺と右辺は独立しているので、片方のみ省略記法とすることもできます。
ただし、選択肢1のように( )のみ省略したり、選択肢3および4のような中途半端な省略記法はできないので注意が必要です。
なお、ここで使用している引数のsは、あくまでラムダ式の中のみがスコープ範囲となりますので、ラムダ式の外からアクセスすることはできないという点にも注意してください。