web-dev-qa-db-ja.com

静的初期化コードブロックと非静的初期化コードブロックの違いは何ですか

私の質問は、静的キーワードの特定の使用法についてです。どの関数にも属さないクラス内のコードブロックをカバーするためにstaticキーワードを使用することは可能です。たとえば、次のコードはコンパイルされます。

public class Test {
    private static final int a;    
    static {
        a = 5;
        doSomething(a);
    }
    private static int doSomething(int x) {
        return (x+5);
    }
}

staticキーワードを削除すると、変数afinalであるため、文句を言われます。ただし、finalstaticの両方のキーワードを削除してコンパイルすることは可能です。

それは私にとって混乱しています。どのメソッドにも属さないコードセクションがあるとしたらどうでしょうか。それを起動するにはどうしたらいいですか?一般に、この使用法の目的は何ですか?もっといいのは、これに関するドキュメントはどこにありますか。

328
Szere Dyeri

静的修飾子を持つコードブロックは、classイニシャライザを表します。静的修飾子なしでは、コードブロックはインスタンス初期化子です。

クラスの初期化子は、クラスがロードされたときに(実際には解決されたときに技術的なことに)定義された順序で実行されます(トップダウン、単純な変数初期化子と同じ)。

インスタンス初期化子は、コンストラクタコードが実行される直前、スーパーコンストラクタが呼び出された直後に、クラスがインスタンス化されたときに定義された順序で実行されます。

int aからstaticを削除すると、それがインスタンス変数になり、静的初期化子ブロックからアクセスすることはできません。これはエラー「非静的変数aは静的コンテキストから参照できません」でコンパイルに失敗します。

Initializerブロックからstaticも削除すると、それがインスタンス初期化子になるため、int aは構築時に初期化されます。

383
Lawrence Dol

いや!スタティックイニシャライザとは?

静的初期化子は、Javaクラス内のstatic {}コードブロックであり、コンストラクタまたはmainメソッドが呼び出される前に1回だけ実行されます。

OK!もっと教えてください...

  • javaクラス内のコードブロックstatic { ... }です。クラスが呼び出されたときに仮想マシンによって実行されます。
  • returnステートメントはサポートされていません。
  • 引数はサポートされていません。
  • thisまたはsuperはサポートされていません。

うーん、どこで使えますか?

あなたが大丈夫と感じているところならどこでも使用することができます:)それは簡単です。しかし、データベース接続、API init、Loggingなどを行うときに使用されることがほとんどです。

単に吠えないで!例はどこにありますか?

package com.example.learnjava;

import Java.util.ArrayList;

public class Fruit {

    static {
        System.out.println("Inside Static Initializer.");

        // fruits array
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Pear");

        // print fruits
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        System.out.println("End Static Initializer.\n");
    }

    public static void main(String[] args) {
        System.out.println("Inside Main Method.");
    }
}

出力???

内部スタティックイニシャライザ.

林檎

オレンジ

静的初期化子を終了します。

内部メインメソッド.

お役に立てれば!

152
Madan Sapkota

staticブロックは「静的初期化子」です。

クラスがロードされたときに自動的に呼び出されます。それを呼び出すための他の方法はありません(Reflection経由でさえ)。

私は個人的にJNIコードを書くときだけこれを使ったことがあります。

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}
54
Alnitak

これは直接からです http://www.programcreek.com/2011/10/Java-class-instance-initializers/

実行順序

次のクラスを見てください、あなたはどちらが最初に実行されるか知っていますか?

public class Foo {

    //instance variable initializer
    String s = "abc";

    //constructor
    public Foo() {
        System.out.println("constructor called");
    }

    //static initializer
    static {
        System.out.println("static initializer called");
    }

    //instance initializer
    {
        System.out.println("instance initializer called");
    }

    public static void main(String[] args) {
        new Foo();
        new Foo();
    }
}

出力:

呼び出された静的初期化子

インスタンス初期化子が呼び出されました

呼び出されたコンストラクタ

インスタンス初期化子が呼び出されました

呼び出されたコンストラクタ

2. Javaインスタンス初期化子はどのように動作しますか?

上記のインスタンス初期化子にはprintlnステートメントが含まれています。それがどのように機能するかを理解するために、それを変数代入ステートメント、例えばb = 0として扱うことができます。これにより、理解しやすくなります。

の代わりに

int b = 0、書くことができます

int b;
b = 0;

したがって、インスタンス初期化子とインスタンス変数初期化子はほとんど同じです。

3.インスタンス初期化子はいつ役に立ちますか?

インスタンス初期化子の使用はめったにありませんが、それでもインスタンス変数初期化子に代わる便利な方法です。

  1. 初期化子コードは例外を処理しなければならない
  2. インスタンス変数イニシャライザでは表現できない計算を実行します。

もちろん、そのようなコードはコンストラクタで書くことができます。しかし、クラスに複数のコンストラクタがある場合は、各コンストラクタでコードを繰り返す必要があります。

インスタンス初期化子を使えば、一度コードを書くだけでよく、オブジェクトを作成するためにどのコンストラクタが使用されてもそれは実行されます。 (これは単なる概念であり、あまり使用されていないと思います。)

インスタンス初期化子が便利なもう1つのケースは、匿名の内部クラスです。これは、コンストラクターをまったく宣言できません。 (これはロギング機能を配置するのに良い場所でしょうか?)

Derheinに感謝します。

インターフェースを実装するAnonymousクラス[1]にはコンストラクタがないことにも注意してください。したがって、インスタンス初期化子は、構築時にあらゆる種類の式を実行するために必要です。

35
Alexei Fando

"final"は、オブジェクト初期化コードの終わりまでに変数が初期化されなければならないことを保証します。同様に "static final"はクラス初期化コードの終わりまでに変数が初期化されることを保証します。初期化コードから "static"を省略すると、それはオブジェクト初期化コードに変わります。したがって、あなたの変数はもはやその保証を満たしていません。

12
DJClayworth

開発者がイニシャライザブロックを使用すると、Javaコンパイラはイニシャライザを現在のクラスの各コンストラクタにコピーします。

例:

次のコード

class MyClass {

    private int myField = 3;
    {
        myField = myField + 2;
        //myField is worth 5 for all instance
    }

    public MyClass() {
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

以下と同等です。

class MyClass {

    private int myField = 3;

    public MyClass() {
        myField = myField + 2;
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        myField = myField + 2;
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

私の例が開発者に理解されていることを願っています。

6
cardman

プログラム内の任意の場所で呼び出す必要がある静的ブロックにコードを書くことはありません。コードの目的を実行することになっている場合は、それをメソッドに入れる必要があります。

クラスがロードされたときに静的変数を初期化するために静的初期化子ブロックを書くことができますが、このコードはもっと複雑になることがあります.

静的初期化子ブロックは、名前、引数、および戻り型のないメソッドのように見えます。あなたは決してそれを呼ばないのでそれは名前を必要としません。呼び出されるのは、仮想マシンがクラスをロードするときだけです。

6

静的コードブロックは、(オブジェクト変数とは対照的に)クラス変数をインスタンス化または初期化するために使用できます。したがって、 "a"静的宣言は、すべてのTestオブジェクトによって共有される1つのみであることを意味し、Testクラスが最初にロードされるときに、作成されるTestオブジェクトの数に関係なく、静的コードブロックは1回だけ "a"を初期化します。

4
Paul Tomblin