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


357

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

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

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

どちらの面でも混乱します。どのメソッドにも属さないコードセクションをどのように持つべきですか?それを呼び出すにはどうすればよいですか?一般的に、この使用法の目的は何ですか?または、これに関するドキュメントはどこにありますか?

回答:


403

static修飾子を含むコードブロックは、クラス初期化子を示します。static修飾子がない場合、コードブロックはインスタンス初期化子です。

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

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

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

staticイニシャライザブロックからも削除すると、インスタンスイニシャライザになり、int a構築時に初期化されます。


静的初期化子は、実際には、クラスが初期化されたときに、ロードされてリンクされた後で呼び出されます。これは、クラスのオブジェクトをインスタンス化するか、クラスの静的変数またはメソッドにアクセスすると発生します。実際には、あなたは、静的初期化子とメソッドを持つクラスを持っている場合はpublic static void staticMethod(){}、あなたが実行している場合、TestStatic.class.getMethod("staticMethod");。静的初期化子は呼び出されません。詳細情報はこちらdocs.oracle.com/javase/specs/jvms/se10/html/...
TOTOを

@Totò:はい、それはクラスの解決に伴うものです(少なくとも、以前は「解決」方法としてlink + initとしてそれを参照していたのです)。私はあなたが物事を発見するためにリフレクションを使用することができます驚かないよについて、それが解決せずにクラスを。
Lawrence Dol 2018年

166

うっ!静的初期化子とは何ですか?

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

OK!もっと教えて...

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

どこで使えますか?

大丈夫だと思うところならどこでも使用できます:)そのシンプルさ。しかし、データベース接続、APIの初期化、ロギングなどを行うときに使用されることがほとんどです。

ただ吠えないで!例はどこですか

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.");
    }
}

出力???

静的初期化子の内部。

林檎

オレンジ

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

Inside Mainメソッド。

お役に立てれば!


マダン、ありがとう!静的ブロックは、代わりに使用することができますafterPropertiesSet()InitializingBean
Alexander Suraphel、2015年

3
はい、できます!静的初期化子は、クラスがjvmによってロードされるときに呼び出されます。つまり、コードが実行される最初のフェーズです。コンストラクタもある場合、順序は次のようになります。静的初期化子、コンストラクタ、afterPropertiesSet
Martin Baumgartner

57

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

クラスが読み込まれると自動的に呼び出され、それを呼び出す方法は他にありません(Reflectionを介しても)。

私は個人的に、これまでJNIコードを作成するときにのみ使用してきました。

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

6
いいえ、明示的に呼び出す方法はありません。クラス初期化子はMethodインスタンスによって表されることはなく、Java仮想マシンによってのみ呼び出されます。
Rafael Winterhalter 2014年

46

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

1.実行順序

次のクラスを見て、どれが最初に実行されるか知っていますか?

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回記述するだけで、オブジェクトの作成にどのコンストラクターを使用しても実行されます。(これは単なる概念であり、あまり使用されていません。)

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

ダーラインに感謝します。

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


12

「最終」は、オブジェクト初期化子コードの終わりの前に変数を初期化する必要があることを保証します。同様に、「静的な最終」は、変数がクラス初期化コードの終わりまでに初期化されることを保証します。初期化コードから「静的」を省略すると、オブジェクトの初期化コードになります。したがって、変数はもはやその保証を満たしません。


8

プログラムの任意の場所で呼び出す必要がある静的ブロックにコードを記述しません。コードの目的を呼び出す場合は、コードをメソッドに配置する必要があります。

静的初期化ブロックを記述して、クラスが読み込まれたときに静的変数を初期化できますが、このコードはより複雑になる可能性があります。

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


6

開発者が初期化ブロックを使用すると、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
    }
}

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


4

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


補足として、オブジェクトのインスタンスを作成せずに、代わりにpublic static関数を呼び出します。このブロックがこのパブリック関数呼び出しの前に実行されることが保証されていることを意味しますか?ありがとう。
Szere Dyeri 2008

クラスのパブリック静的関数を呼び出す場合は、クラスを最初にロードする必要があるため、静的イニシャライザが最初に実行されます。
ポールトンブリン

それを使用しようとしているコードを(間接的に)呼び出すクラスの初期化である場合を除きます。IFYSWIM。循環依存関係など。
トム・ホーティン-2008

1
@Tomは正しい-別の静的イニシャライザが呼び出される前に、ある静的イニシャライザが静的メソッドを呼び出すようなものを書くことは可能ですが、私の心は考えに反動するので、私はそれを考慮しませんでした。
ポールトンブリン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.