クラス・継承などの理解を深める演習

総まとめ試験用の類似問題を演習する。

内容をよく理解し、類題を解けるように練習しておくこと。


演習17

次のプログラムをクラス Exercise1 の main から実行したとき表示される結果を、パソコンを使わず自分で動作を考えて、示しなさい。

public class Exercise1 {
    public static void main(String[] args) {
        Aa x = new Aa();
        // ①
        x.countUp12();
        // ②
        x.countUp();
        // ③
        System.out.println( "count1=" + x.getCount1()
                + "  count2=" + x.getCount2()
                + "  count3=" + x.getCount3() );
        Bb y = new Aa();
        // ④
        y.countUp12();
        // ⑤
        y.countUp();
        // ⑥
        System.out.println( "count1=" + y.getCount1()
                + "  count2=" + y.getCount2() );
    }
}

class Aa extends Bb {
    static int count3 = 7;
    private String s = "Class Aa";
    Aa() { System.out.println( s ); }
    static void countUp() { count3 += 10; }
    void countUp12() {
        count1 += 17;
        count2 += 17;
    }
    static int getCount3() { return count3; }
}

class Bb {
    static int count1;
    int count2 = count1 + 5;
    private String s = "Class Bb";
    Bb() { System.out.println( s ); }
    void countUp12() {
        count1 += 13;
        count2 += 13;
    }
    static void countUp() { count1 += 3; }
    static int getCount1() { return count1; }
    int getCount2() { return count2; }
}

上のコードをコピー&ペーストし、Exercise1.java の名前で保存し、コンパイル・実行して出力を確かめなさい。
出力は以下のようになるはずである。

Exercise1出力

クラス Aa, Bb の分析

クラス Exercise1 はメインとして実行されるクラスで、その中でクラス Aa, Bb を利用している。両クラスを分析する必要がある。以下のように、フィールドおよびメソッドをstaticかどうかで区別して列挙しておく。

●クラス Aa

クラス Bb のサブクラス
インスタンス フィールド(変数)s
staticフィールド(変数)count3
インスタンス メソッドcountUp12
staticメソッドcountUp
getCount3

●クラス Bb

クラス Aa のスーパークラス
インスタンス フィールド(変数)count2
s
staticフィールド(変数)count1
インスタンス メソッドcountUp12
getCount2
staticメソッドcountUp
getCount1

動作を考える際、基本として把握しておくべきことがある。

変数
static変数はクラスに対して1個だけ。インスタンスがなくても存在。
インスタンス変数 (staticでないもの) は各インスタンスに対して1個ずつ別個に存在。
メソッド
staticメソッドは、静的な型(宣言の型)に応じて呼び出される。インスタンスがなくても呼び出せる。
インスタンスメソッドは、インスタンスの実際の型に応じて呼び出される。(ポリモフィズム)
クラスの初期化
クラスのstatic変数は、そのクラスが初めて使用される時点 (クラスの初期化時点) で、一度だけ初期化される。
クラスの初期化では、static変数の初期化およびstatic初期化ブロックの実行がソースに記述されている順番でなされていく。
インスタンス生成
最初にスーパークラスが生成される。(クラスの中味の設定およびコンストラクタの実行)
その後で、当該クラスの中味が設定され、最後にコンストラクタが実行される。
クラスの中味の設定では、インスタンス変数の初期化およびインスタンス初期化ブロックの実行がソースに記述されている順番でなされていく。

上記コードでは、staticメソッドがインスタンス変数を介して呼び出されるかのように記述されている。Java では文法的に許されてはいるが推奨されない記述法である。
しかし実際には、staticメソッドはインスタンスには無関係でクラスに結びついている。 実用的なコードでは、staticメソッド呼び出しは、下記の赤色部分のように、クラスを明示して呼び出すべきである。

public class Exercise1 {
    public static void main(String[] args) {
        Aa x = new Aa();
        x.countUp12();
        Aa.countUp();    // x.countUp では、x が Aa 型として宣言されているので
        System.out.println( "count1=" + Bb.getCount1()   // x.getCount1 では、Aa には getCount1 がなく、スーパークラスの Bb に getCount1 があるので
                + "  count2=" + x.getCount2()
                + "  count3=" + Aa.getCount3() );        // x.getCount3 書き換え
        Bb y = new Aa();
        y.countUp12();
        Bb.countUp();    //  y.countUp では、y が Bb 型として宣言されているので
        System.out.println( "count1=" + Bb.getCount1()   //  y.getCount1 書き換え
                + "  count2=" + y.getCount2() );
    }
}

プログラムの詳細動作を説明する図は下のようになる。ソースコード上の①~⑥に来る直前に設定される変数の変化を矢印で示している。

なお、static変数の初期化について補足しておく。
①の直前行でクラス Aa およびそのスーパークラスであるクラス Bb が初めて使用されるため、①の直前行のコードを実行する前に、クラス Bb の初期化およびクラス Aa の初期化が順に実行される。すなわち、クラス Bb およびクラス Aa の static 変数の値設定がこの時行われる。
その結果、クラス Bb の count1 は 0、クラス Aa の count3 は 7 に値設定される。

プログラムの詳細動作


演習18

次のプログラムをクラス Exercise2 の main から実行したとき表示される結果を示しなさい。

Exercise2ソースコード

演習17でやったのと同様に、クラス Ak, Bk のフィールドおよびメソッドをstaticの有無で区別して列挙しなさい。

●クラス Ak

クラス Bk のスーパークラス
インスタンス フィールド(変数)
staticフィールド(変数)
インスタンス メソッド
staticメソッド

●クラス Bk

クラス Ak のサブクラス
インスタンス フィールド(変数)
staticフィールド(変数)
インスタンス メソッド
staticメソッド

自分で結果を考えた後、実際にプログラムを作成・実行し、出力を確かめなさい。

その出力結果を説明できるよう、演習17で示したのと同様な、プログラム動作の詳細説明図を作りなさい。ソースコード上の①~⑥に来る直前に設定される変数の変化を矢印で示すこと。

変数、メソッドの分類