Javaクラスの初期化処理

継承されたクラスを初期化するときに、初期化の順序を忘れるのでメモ。

結論

  1. static初期化コード(ただし初回のクラスロード時のみ)
  2. 初期化コード
  3. コンストラクタ

継承している場合は親(スーパークラス)から順に初期化され、利用するクラスが最後に初期化される。子が親のコンストラクタを明示的に呼ばずとも、親のコンストラクタは実行される。

ここで「初期化コード」と記しているものは、クラス内にブラケットを置くブロックとフィールドの2つを指す。これらはソースコード上で記述されている順番に評価される。

Javaのソースコードはコードの記述順に縛られる実装は避けることが好ましいとされている(要出展)。ブロックもフィールドもコンストラクタ内に実装することで代用は効く。クラスの初期化時に見えづらくなる初期化ブロックの利用は避けたほうが良いと考えられる。

なお、この処理順序はJava言語仕様の初期化処理に明記されている。

検証コード

エントリポイントから素直にChildクラスを2回作るものとした。フィールドとブロックが記述順に依存していることを示すため、ChildクラスとParentクラスで順番を入れ替えている。フィールドが評価されたタイミングを知るためにAgeクラスを準備している。

public class Main {
    // プログラムのエントリポイント
    public static void main(String[] args) {
        System.out.println("main - start");
        new Child();
        System.out.println("main - second");
        new Child();
        System.out.println("main - end");
    }

    // 親クラス(スーパークラス)
    public static class Parent {
        // 静的フィールド
        private static final Age staticAge = new Age(20, "Parent static");
        // フィールド
        private final Age fieldAge = new Age(53, "Parent field");

        // 初期化ブロック
        {
            System.out.println("Parent - init block");
        }

        // 静的初期化ブロック
        static {
            System.out.println("Parent - static init block");
        }

        // コンストラクタ
        public Parent() {
            System.out.println("Parent - constructor");
        }
    }

    // 子クラス
    public static class Child extends Parent {
        // コンストラクタ
        public Child() {
            System.out.println("Child - constructor");
        }

        // 初期化ブロック
        {
            System.out.println("Child - init block");
        }

        // 静的初期化ブロック
        static {
            System.out.println("Child - static init block");
        }

        // 静的フィールド
        private static final Age staticAge = new Age(12, "Child static");
        // フィールド
        private final Age fieldAge = new Age(25, "Child field");
    }

    // フィールド評価の判定用クラス
    public static class Age {
        public Age(int age, String type) {
            System.out.println("Age - constructor:" + type);
        }
    }
}

実行結果

検証コードをコンパイルし実行した結果は次の通り。実行結果は先の結論にまとめている。

% /usr/local/jdk1.8.0_25/bin/javac Main.java
% /usr/local/jdk1.8.0_25/bin/java Main
main - start
Age - constructor:Parent static
Parent - static init block
Child - static init block
Age - constructor:Child static
Age - constructor:Parent field
Parent - init block
Parent - constructor
Child - init block
Age - constructor:Child field
Child - constructor
main - second
Age - constructor:Parent field
Parent - init block
Parent - constructor
Child - init block
Age - constructor:Child field
Child - constructor
main - end

参考サイト

java – Initialize field before super constructor runs? – Stack Overflow


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *