静的初期化ブロック


265

私が理解している限り、「静的初期化ブロック」は、1行で実行できない場合に静的フィールドの値を設定するために使用されます。

しかし、そのために特別なブロックが必要な理由がわかりません。たとえば、フィールドを静的(値の割り当てなし)として宣言します。次に、上記で宣言した静的フィールドに値を生成して割り当てるコードの数行を記述します。

なぜ次のような特別なブロックにこの行が必要なのですstatic {...}か?


6
マイナーなフィードバックですが、仮定を明確に述べて、正しい答えを明確にしていただければ助かります。私が最初にあなたの質問を読んだとき、私は誤解し、あなたは{...}vs の違いを知っていると思いましたstatic {...}。(その場合、ジョンスキートは間違いなくあなたの質問により適切に答えました)
David T.

1
この質問は非常に不明確です。あなたは、回答者がスクランブルをかけ、あなたが何を意味しているのかについて多くの長い間推測を行っています。考えている静的な初期化ブロックの例と代替案を明示的に書き出して、人々が明確に答えられるようにしますか?
ドンハッチ

回答:


430

非静的ブロック:

{
    // Do Something...
}

クラスのインスタンスが構築されるたびに呼び出さます。静的ブロックはのみ呼び出されます一度クラス自体が初期化されたときにそのタイプの多くのオブジェクトは、作成どんなに、、。

例:

public class Test {

    static{
        System.out.println("Static");
    }

    {
        System.out.println("Non-static block");
    }

    public static void main(String[] args) {
        Test t = new Test();
        Test t2 = new Test();
    }
}

これは印刷します:

Static
Non-static block
Non-static block

107
なぜこれが受け入れられた答えなのですか?それは質問に答えさえしません。
ポールベッロラ2011

43
「クラスが構築されるたびに呼び出されます。静的ブロックは、そのタイプのオブジェクトをいくつ作成しても、1回だけ呼び出されます。」という質問に答えます。
アダムArold

83
好奇心旺盛な読者のために、非静的ブロックは実際には、Javaコンパイラーによってクラスが持つすべてのコンストラクター(source)にコピーされます。したがって、フィールドを初期化するのは、依然としてコンストラクターの仕事です。
マーティンアンダーソン、2013

2
受け入れられた答えはこれであるはずです:stackoverflow.com/a/2420404/363573。この回答は、静的ブロックが必要な実際の例を示しています。
ステファン

16
なぜこの回答が突然反対投票になったのですか?あなたはこれが受け入れられた答えであることに同意しないかもしれませんが、それは確かに決して間違っていたり、誤解を招くものではありません。それは単純な例でこれらの言語構造の理解を助けることを試みているだけです。
Frederik Wordenskjold、2013

133

それらが静的初期化ブロックになかった場合、どこにありますか?初期化の目的でローカルであるだけの変数をどのように宣言し、フィールドと区別しますか?例えば、どのようになりますが書きたいです:

public class Foo {
    private static final int widgets;

    static {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        widgets = first + second;
    }
}

ブロック内になかった場合firstsecondフィールドのように見えます。それらがstatic前にないブロック内にある場合、それは静的初期化ブロックではなくインスタンス初期化ブロックとしてカウントされるため、合計で1回ではなく、構築されたインスタンスごとに1回実行されます。

この特定のケースでは、代わりに静的メソッドを使用できます。

public class Foo {
    private static final int widgets = getWidgets();

    static int getWidgets() {
        int first = Widgets.getFirstCount();
        int second = Widgets.getSecondCount();
        // Imagine more complex logic here which really used first/second
        return first + second;
    }
}

...しかし、同じブロック内に割り当てる複数の変数がある場合、または何もない場合(たとえば、何かをログに記録したい場合、またはネイティブライブラリを初期化する場合)は機能しません。


1
静的ブロックは、静的変数が割り当てられる前または後に発生しますか?private static int widgets = 0; static{widgets = 2;}
Weishi Zeng 2014

1
静的変数が割り当てられる前または後に静的ブロックが発生するかどうかについて知りました。たとえばprivate static int widgets = 0; static{widgets = 2;}、「=」の割り当てが順番に行われることがわかりました。つまり、最初に置かれた「=」が最初に割り当てられます。上記の例では、「ウィジェット」の値は2になります(PSはコメントが5分でしか編集できないことを知りませんでした...)
Weishi Zeng

@WeishiZeng:はい、これはdocs.oracle.com/javase/specs/jls/se8/html/…に記載されています -ポイント9
Jon Skeet

しかし、静的初期化ブロックとまったく同じコードを持つプライベート静的メソッドを使用して、ウィジェットをプライベート静的メソッドに割り当てることもできませんか?
Zachary Kraus

1
@Zachary:値を返し、メソッド呼び出しの結果を割り当てるということですか?もしそうなら、はい- ブロックの結果としてちょうど1つの変数に割り当てているとき。約7時間で詳細を含む私の回答を編集します...
Jon Skeet

103

次に例を示します。

  private static final HashMap<String, String> MAP = new HashMap<String, String>();
  static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

「静的」セクションのコードは、クラスのロード時に、クラスのインスタンスが構築される前(および静的メソッドが他の場所から呼び出される前)に実行されます。これにより、クラスリソースがすべて使用できる状態になっていることを確認できます。

非静的な初期化ブロックを持つことも可能です。これらは、クラスに定義されたコンストラクタメソッドのセットに対する拡張のように機能します。キーワード "static"が省略されていることを除いて、それらは静的初期化子ブロックと同じように見えます。


4
その特定の例では、二重ブレースパターンが「乱用」されている場合があります:)
BalusC

それは悪用される可能性がありますが、一方で、いくつかの混乱をクリーンアップし、ある種のコードをもう少し「堅固」にします。私は楽しみのためにErlangでプログラミングしていて、ローカル変数を必要としないことに夢中になっています:-)
Pointy

1
<<「静的」セクションのコードは、クラスのロード時に、クラスのインスタンスが構築される前(および静的メソッドが他の場所から呼び出される前)に実行されます。これにより、クラスリソースがすべて使用できる状態になっていることを確認できます。>>(上記の回答で言及されている「先のとがった」)これは、静的ブロックの実行に関して注意すべき非常に重要なポイントです。
学習者

afterPropertiesSetメソッドの後にInitializingBeanを使用してこれを行うことはできますか?
egemen

48

また、実行時に一部のクラスを1回だけロードするなど、実際には値を何にも割り当てたくない場合にも役立ちます。

例えば

static {
    try {
        Class.forName("com.example.jdbc.Driver");
    } catch (ClassNotFoundException e) {
        throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
    }
}

ねえ、別の利点があります。それを使用して例外を処理できます。getStuff()ここException実際にcatchブロックに属しているをスローすると想像してください。

private static Object stuff = getStuff(); // Won't compile: unhandled exception.

次に、staticイニシャライザが役立ちます。そこで例外を処理できます。

別の例は、割り当ての間に実行できないことを後で行うことです:

private static Properties config = new Properties();

static {
    try { 
        config.load(Thread.currentThread().getClassLoader().getResourceAsStream("config.properties");
    } catch (IOException e) {
        throw new ExceptionInInitializerError("Cannot load properties file.", e);
    }
}

JDBCドライバーの例に戻ると、適切なJDBCドライバー自体もstaticイニシャライザを使用してに自身を登録しますDriverManagerこれこの答えも見てください。


2
ここに危険なブードゥーがあります...静的イニシャライザは、暗黙的に同期される合成clinit()メソッドで実行されます。つまり、JVMは問題のクラスファイルのロックを取得します。これにより、2つのクラスが互いにロードを試み、それぞれが異なるスレッドでロードを開始すると、マルチスレッド環境でデッドロックが発生する可能性があります。www-01.ibm.com/support/docview.wss?uid=swg1IV48872
Ajax

@Ajax:これは、問題のJDBCドライバー、またはそれをロードするアプリケーションコードのバグと考えています。通常、まともなJDBCドライバーの場合、アプリケーションの起動時にアプリケーション全体で一度だけロードする限り、何も問題はありません。
BalusC 2016年

それは確かにバグですが、JDBCドライバーのせいではありません。たぶん、ドライバには無害な独自の静的初期化子があり、おそらくアプリ内の他のいくつかと一緒にこのクラスを無邪気に初期化しているかもしれません。そうでない場合、いくつかの予期しないクラスが互いに循環的にロードし、アプリのデッドロックが発生しています。java.awt.AWTEventとsun.util.logging.PlatformLoggerの間のデッドロックのおかげで、これを発見しました。ヘッドレスで実行するように指示するためにAWTEventに触れただけで、AWTEventもロードするPlatformLogger ...
Ajax

1
両方のクラスが異なるスレッドで同期して終了し、私のビルドは約1/150の実行でデッドロックしました。そのため、静的ブロックでのクラスローディングについては、より注意深くなっています。上記の場合、遅延プロバイダーパターンを使用して、中間プロバイダークラスをすぐに(デッドロックの可能性なしに)作成し、フィールドを初期化してから、実際に(非同期フィールドアクセスで)アクセスしたときに、次に、デッドロックを引き起こす可能性のあるクラスを実際にロードします。
Ajax

11

static blockは単に構文上の砂糖だと思います。あなたができることは何もありませんstaticブロックでありません。

ここに掲載されているいくつかの例を再利用するには。

このコードは、使用せずに書き直すことができます staticイニシャライザます。

方法#1:あり static

private static final HashMap<String, String> MAP;
static {
    MAP.put("banana", "honey");
    MAP.put("peanut butter", "jelly");
    MAP.put("rice", "beans");
  }

方法#2:なし static

private static final HashMap<String, String> MAP = getMap();
private static HashMap<String, String> getMap()
{
    HashMap<String, String> ret = new HashMap<>();
    ret.put("banana", "honey");
    ret.put("peanut butter", "jelly");
    ret.put("rice", "beans");
    return ret;
}

10

存在する必要があるのには、いくつかの実際の理由があります。

  1. 初期化でstatic final例外がスローされる可能性のあるメンバーの初期化
  2. static final計算された値でメンバーを初期化する

人々はstatic {}、特定のクラス(JDBCドライバーなど)が確実にロードされるようにするなど、ランタイム内でクラスが依存するものを初期化する便利な方法としてブロックを使用する傾向があります。それは他の方法で行うことができます。ただし、上記で述べた2つのことは、static {}ブロックのような構造体でのみ実行できます。


8

オブジェクトが静的ブロックで構築される前に、クラスに対してコードのビットを1回実行できます。

例えば

class A {
  static int var1 = 6;
  static int var2 = 9;
  static int var3;
  static long var4;

  static Date date1;
  static Date date2;

  static {
    date1 = new Date();

    for(int cnt = 0; cnt < var2; cnt++){
      var3 += var1;
    }

    System.out.println("End first static init: " + new Date());
  }
}

7

静的ブロックは静的フィールドにしかアクセスできないと考えるのはよくある誤解です。このために、私が実際のプロジェクトでかなり頻繁に使用するコードの一部を以下に示します(少し異なるコンテキストで別の回答から部分的にコピーされます):

public enum Language { 
  ENGLISH("eng", "en", "en_GB", "en_US"),   
  GERMAN("de", "ge"),   
  CROATIAN("hr", "cro"),   
  RUSSIAN("ru"),
  BELGIAN("be",";-)");

  static final private Map<String,Language> ALIAS_MAP = new HashMap<String,Language>(); 
  static { 
    for (Language l:Language.values()) { 
      // ignoring the case by normalizing to uppercase
      ALIAS_MAP.put(l.name().toUpperCase(),l); 
      for (String alias:l.aliases) ALIAS_MAP.put(alias.toUpperCase(),l); 
    } 
  } 

  static public boolean has(String value) { 
    // ignoring the case by normalizing to uppercase
    return ALIAS_MAP.containsKey(value.toUpper()); 
  } 

  static public Language fromString(String value) { 
    if (value == null) throw new NullPointerException("alias null"); 
    Language l = ALIAS_MAP.get(value); 
    if (l == null) throw new IllegalArgumentException("Not an alias: "+value); 
    return l; 
  } 

  private List<String> aliases; 
  private Language(String... aliases) { 
    this.aliases = Arrays.asList(aliases); 
  } 
} 

ここでは、イニシャライザを使用してインデックス(ALIAS_MAP)を維持し、エイリアスのセットを元の列挙型にマッピングします。これは、によって提供される組み込みのvalueOfメソッドの拡張として意図されています。Enumそれ自体がます。

ご覧のとおり、静的イニシャライザはprivateフィールドにもアクセスしますaliasesstaticブロックは既にEnum値のインスタンス(などENGLISH)にアクセスできることを理解することが重要です。これは、ブロックが呼び出される前にフィールドがインスタンスで初期化されたかのように、タイプの場合の初期化と実行の順序Enumが原因です。static privatestatic

  1. Enum暗黙の静的フィールドは定数。これには、Enumコンストラクターとインスタンスブロック、およびインスタンスの初期化も最初に発生する必要があります。
  2. static 発生順に静的フィールドのブロックと初期化を行います。

この順不同の初期化(staticブロックの前のコンストラクター)は注意することが重要です。シングルトンと同様にインスタンスで静的フィールドを初期化するときにも発生します(簡略化)。

public class Foo {
  static { System.out.println("Static Block 1"); }
  public static final Foo FOO = new Foo();
  static { System.out.println("Static Block 2"); }
  public Foo() { System.out.println("Constructor"); }
  static public void main(String p[]) {
    System.out.println("In Main");
    new Foo();
  }
}

次の出力が表示されます。

Static Block 1
Constructor
Static Block 2
In Main
Constructor

明らかなのは、静的な初期化は実際はコンストラクタの、さらには次の後にも発生する可能性があることです。

mainメソッドでFooにアクセスするだけで、クラスがロードされ、静的な初期化が開始されます。しかし、静的初期化の一部として、静的フィールドのコンストラクターを再度呼び出します。その後、静的初期化を再開し、メインメソッド内から呼び出されたコンストラクターを完了します。通常のコーディングでは対処しなくてよいと思うかなり複雑な状況。

詳細については、「Effective Java」を参照してください。


1
にアクセスできてaliasesも、静的ブロックが非静的メンバーにアクセスできるわけではありません。/ static / メソッドによって返されaliasesLanguage値を介してアクセスされますvalues()。あなたが言及したように、その時点で列挙変数がすでに利用可能であるという事実は異常なビットです-通常のクラスの非静的メンバーはこの状況ではアクセスできません。
Ignazio

静的ブロックはまだ静的フィールド(enum ENGLISH、GERMANなどの場合)のみにアクセスしており、この場合はオブジェクトです。静的フィールドはオブジェクトそのものなので、静的オブジェクトのインスタンスフィールドにアクセスできます。
スワミPR

1
class Foo { static final Foo Inst1; static final Foo Inst2; static{ Inst1 = new Foo("Inst1"); Inst2 = new Foo("Inst2"); } static { System.out.println("Inst1: " + Inst1.member); System.out.println("Inst2: " + Inst2.member); } private final String member; private Foo(String member){ this.member = member; } } 上記のコードは列挙型の例と同じで、静的ブロック内のインスタンス変数へのアクセスを許可しています
Swami PR

@SwamiPRは確かにコンパイルされますが、驚いたことに、コードは原則として同じであることに同意する必要があります。私はJava仕様を再読する必要がありますが、見逃しているものがあると感じています。良い返事、ありがとう。
ヨーヨー

@SwamiPR問題は本当に使用する必要があることEnumです。これは、単一のインスタンスを指していることを保証するための最良の方法です- ここを参照してください。そして、あなたの要点として、私はいくつかの更新を行いました。
YoYo

3

静的変数を実行時に設定する必要がある場合は、static {...}ブロックが非常に役立ちます。

たとえば、静的メンバーを設定ファイルまたはデータベースに格納されている値に設定する必要がある場合。

Map最初のメンバー宣言でこれらの値を追加できないため、静的メンバーに値を追加する場合にも役立ちます。


3

したがって、静的フィールド(クラスのインスタンスではなくクラスに属しているため、「クラス変数」とも呼ばれます。つまり、オブジェクトではなくクラスに関連付けられています)を初期化する必要があります。したがって、このクラスのインスタンスを作成せず、この静的フィールドを操作したい場合は、次の3つの方法で行うことができます。

1-変数を宣言するときに初期化するだけです。

static int x = 3;

2-静的な初期化ブロックがあります。

static int x;

static {
 x=3;
}

3-クラス変数にアクセスして初期化するクラスメソッド(静的メソッド)を用意します。これは、上記の静的ブロックの代替です。あなたはプライベート静的メソッドを書くことができます:

public static int x=initializeX();

private static int initializeX(){
 return 3;
}

では、なぜ静的メソッドの代わりに静的初期化ブロックを使用するのでしょうか。

それは実際にあなたがあなたのプログラムで必要とするもの次第です。ただし、静的初期化ブロックが1回呼び出され、クラスメソッドの唯一の利点は、クラス変数を再初期化する必要がある場合に、後で再利用できることです。

プログラムに複雑な配列があるとします。初期化すると(たとえばforループを使用して)、この配列の値はプログラム全体で変化しますが、ある時点で再初期化したい(初期値に戻る)必要があります。この場合、プライベート静的メソッドを呼び出すことができます。プログラムで値を再初期化する必要がない場合は、静的ブロックを使用できます。プログラムで後で使用しないため、静的メソッドは必要ありません。

注:静的ブロックは、コードに表示される順序で呼び出されます。

例1:

class A{
 public static int a =f();

// this is a static method
 private static int f(){
  return 3;
 }

// this is a static block
 static {
  a=5;
 }

 public static void main(String args[]) {
// As I mentioned, you do not need to create an instance of the class to use the class variable
  System.out.print(A.a); // this will print 5
 }

}

例2:

class A{
 static {
  a=5;
 }
 public static int a =f();

 private static int f(){
  return 3;
 }

 public static void main(String args[]) {
  System.out.print(A.a); // this will print 3
 }

}

0

補足として、@ Pointyが言ったように

「静的」セクションのコードは、クラスのロード時に、クラスのインスタンスが構築される前(および静的メソッドが他の場所から呼び出される前)に実行されます。

System.loadLibrary("I_am_native_library")静的ブロックに追加することになっています。

static{
    System.loadLibrary("I_am_a_library");
}

これは、関連するライブラリがメモリにロードされる前にネイティブメソッドが呼び出されないことを保証します。

オラクルのloadLibraryによると:

このメソッドが同じライブラリ名で複数回呼び出された場合、2番目以降の呼び出しは無視されます。

そのため、予期せぬことに、ライブラリが複数回ロードされるのを防ぐためにSystem.loadLibraryを使用することはありません。


0

最初java.class.Classに、実行時にアプリケーションクラス自体がオブジェクトにインスタンス化されることを理解する必要があります。これは、静的ブロックが実行されるときです。したがって、実際にこれを行うことができます:

public class Main {

    private static int myInt;

    static {
        myInt = 1;
        System.out.println("myInt is 1");
    }

    //  needed only to run this class
    public static void main(String[] args) {
    }

}

そして、 "myInt is 1"をコンソールに出力します。クラスをインスタンス化していないことに注意してください。


0
static int B,H;
static boolean flag = true;
static{
    Scanner scan = new Scanner(System.in);
    B = scan.nextInt();
    scan.nextLine();
    H = scan.nextInt();

    if(B < 0 || H < 0){
        flag = false;
        System.out.println("java.lang.Exception: Breadth and height must be positive");
    } 
}

-1

静的ブロックは、動的な方法で静的データメンバーを初期化するためのテクノロジで使用されます。または、静的データメンバーの動的初期化で静的ブロックが使用されていると言えます。非静的データメンバーの初期化では、コンストラクターがあるため、静的データメンバーを動的に初期化できる場所

Eg:-class Solution{
         // static int x=10;
           static int x;
       static{
        try{
          x=System.out.println();
          }
         catch(Exception e){}
        }
       }

     class Solution1{
      public static void main(String a[]){
      System.out.println(Solution.x);
        }
        }

これで、静的int xは動的に初期化されます。コンパイラがSolution.xに移動すると、Bcozはクラスのロード時にソリューションクラスと静的ブロックのロードをロードします。したがって、その静的データメンバーを動的に初期化できます。

}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.