106

そのため、最近 Java のスキルを磨いていて、以前は知らなかった機能をいくつか発見しました。静的およびインスタンス初期化子は、そのような 2 つの手法です。

私の質問は、コンストラクターにコードを含める代わりに、いつイニシャライザーを使用するのですか? 私はいくつかの明白な可能性を考えました:

  • 静的/インスタンス初期化子を使用して、「最終的な」静的/インスタンス変数の値を設定できますが、コンストラクターはできません

  • 静的初期化子を使用して、クラス内の任意の静的変数の値を設定できます。これは、各コンストラクターの先頭に「if (someStaticVar == null) // do stuff」コード ブロックを配置するよりも効率的です。

これらのケースは両方とも、これらの変数を設定するために必要なコードが単純な「var = value」よりも複雑であると想定しています。そうでない場合、変数を宣言するときに単に値を設定する代わりに初期化子を使用する理由がないように思われます。

ただし、これらは些細な利点 (特に最終変数を設定する機能) ではありませんが、初期化子を使用する必要がある状況はかなり限られているようです。

確かに、コンストラクターで行われる多くのことに対して初期化子を使用できますが、そうする理由は本当にわかりません。クラスのすべてのコンストラクターが大量のコードを共有している場合でも、プライベートな initialize() 関数を使用する方が、初期化子を使用するよりも理にかなっているように思えます。コンストラクタ。

何か不足していますか?イニシャライザを使用する必要がある状況は他にもたくさんありますか? それとも、非常に特定の状況で使用するためのかなり限定的なツールですか?

4

10 に答える 10

62

cletus が述べたように、静的初期化子は便利であり、私はそれらを同じ方法で使用します。クラスのロード時に初期化される静的変数がある場合は、特に複雑な初期化を実行し、静的変数を be のままにすることができるため、静的初期化子が適していますfinal。これは大きな勝利です。

「if (someStaticVar == null) // do stuff」は面倒でエラーが発生しやすいと思います。静的に初期化されて宣言されている場合はfinal、それが である可能性を回避できますnull

ただし、次のように言うと混乱します。

静的/インスタンス初期化子を使用して、「最終的な」静的/インスタンス変数の値を設定できますが、コンストラクターはできません

私はあなたが両方を言っていると仮定します:

  • 静的初期化子を使用して「最終的な」静的変数の値を設定できますが、コンストラクターは使用できません
  • インスタンス初期化子を使用して「最終」インスタンス変数の値を設定できますが、コンストラクターはできません

あなたは最初の点では正しく、2 番目の点では間違っています。たとえば、次のことができます。

class MyClass {
    private final int counter;
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

また、多くのコードがコンストラクター間で共有されている場合、これを処理する最善の方法の 1 つは、コンストラクターをチェーンして、既定値を提供することです。これにより、何が行われているかがかなり明確になります。

class MyClass {
    private final int counter;
    public MyClass() {
        this(0);
    }
    public MyClass(final int counter) {
        this.counter = counter;
    }
}
于 2009-04-29T22:56:54.673 に答える
59

匿名内部クラスは (匿名であるため) コンストラクターを持つことができないため、インスタンス初期化子に非常に自然に適合します。

于 2009-04-29T22:55:48.450 に答える
27

私はほとんどの場合、最終的な静的データ、特にコレクションを設定するために静的初期化ブロックを使用します。例えば:

public class Deck {
  private final static List<String> SUITS;

  static {
    List<String> list = new ArrayList<String>();
    list.add("Clubs");
    list.add("Spades");
    list.add("Hearts");
    list.add("Diamonds");
    SUITS = Collections.unmodifiableList(list);
  }

  ...
}

この例は、1 行のコードで実行できます。

private final static List<String> SUITS =
  Collections.unmodifiableList(
    Arrays.asList("Clubs", "Spades", "Hearts", "Diamonds")
  );

しかし、特にアイテムの初期化が簡単でない場合は、静的バージョンの方がはるかにきれいです。

ナイーブな実装では、修正不可能なリストが作成されない場合もありますが、これは潜在的な間違いです。上記は、パブリック メソッドなどから喜んで返すことができる不変のデータ構造を作成します。

于 2009-04-29T22:44:47.277 に答える
16

ここですでにいくつかの優れた点を追加するだけです。静的初期化子はスレッドセーフです。クラスがロードされたときに実行されるため、静的データが初期化されているかどうかを確認してから実際に初期化するために同期ブロックが必要なコンストラクターを使用するよりも、静的データの初期化が簡単になります。

public class MyClass {

    static private Properties propTable;

    static
    {
        try 
        {
            propTable.load(new FileInputStream("/data/user.prop"));
        } 
        catch (Exception e) 
        {
            propTable.put("user", System.getProperty("user"));
            propTable.put("password", System.getProperty("password"));
        }
    }

public class MyClass 
{
    public MyClass()
    {
        synchronized (MyClass.class) 
        {
            if (propTable == null)
            {
                try 
                {
                    propTable.load(new FileInputStream("/data/user.prop"));
                } 
                catch (Exception e) 
                {
                    propTable.put("user", System.getProperty("user"));
                    propTable.put("password", System.getProperty("password"));
                }
            }
        }
    }

インスタンス レベルではなく、クラスで同期する必要があることを忘れないでください。これにより、クラスがロードされるときの 1 回限りのコストではなく、インスタンスが構築されるたびにコストが発生します。さらに、それは醜いです;-)

于 2009-04-30T13:29:34.683 に答える
13

初期化子とそのコンストラクターの初期化順序に対する答えを探して、記事全体を読みました。見つからなかったので、理解を確認するためにいくつかのコードを書きました。この小さなデモンストレーションをコメントとして追加すると思いました。理解度をテストするために、一番下を読む前に答えを予測できるかどうかを確認してください。

/**
 * Demonstrate order of initialization in Java.
 * @author Daniel S. Wilkerson
 */
public class CtorOrder {
  public static void main(String[] args) {
    B a = new B();
  }
}

class A {
  A() {
    System.out.println("A ctor");
  }
}

class B extends A {

  int x = initX();

  int initX() {
    System.out.println("B initX");
    return 1;
  }

  B() {
    super();
    System.out.println("B ctor");
  }

}

出力:

java CtorOrder
A ctor
B initX
B ctor
于 2012-01-30T01:35:59.173 に答える
7

静的初期化子は、静的コンテキストのコンストラクターと同等です。確かに、インスタンス初期化子よりも頻繁に表示されます。コードを実行して静的環境をセットアップする必要がある場合があります。

一般に、インスタンス初期化子は、匿名の内部クラスに最適です。JMockのクックブックを見て、それを使用してコードをより読みやすくする革新的な方法を確認してください。

場合によっては、コンストラクター間でチェーンするのが複雑なロジックがある場合 (たとえば、サブクラス化していて、super() を呼び出す必要があるため、this() を呼び出すことができない場合)、インスタンスで共通の処理を行うことで重複を回避できます。初期化子。ただし、インスタンス初期化子は非常にまれであるため、多くの人にとって驚くべき構文であるため、私はそれらを避け、コンストラクターの動作が必要な場合は、クラスを匿名ではなく具体的​​なものにします。

JMock は例外です。これは、フレームワークの使用方法が意図されているためです。

于 2009-04-29T23:31:01.203 に答える
4

また、上記の素晴らしい回答すべてに加えて、1 つのポイントを追加したいと思います。Class.forName("") を使用して JDBC でドライバーをロードすると、クラスのロードが行われ、Driver クラスの静的初期化子が起動され、その中のコードがドライバーをドライバー マネージャーに登録します。これは、静的コード ブロックの重要な用途の 1 つです。

于 2011-06-18T14:29:46.403 に答える
3

あなたが言ったように、それは多くの場合役に立たず、あまり使用されていない構文と同様に、あなたのコードを見ている次の人が 30 秒を費やしてボールトからコードを引き出すのを止めるためだけに、おそらくそれを避けたいと思うでしょう.

一方で、それがいくつかのことを行う唯一の方法です (それらについてはほとんど説明したと思います)。

いずれにせよ、静的変数自体はいくらか避けるべきです-常にではありませんが、それらを大量に使用する場合、または1つのクラスで大量に使用する場合、異なるアプローチを見つけるかもしれません.将来のあなたはあなたに感謝します.

于 2009-04-29T22:44:22.487 に答える