私が理解している限り、「静的初期化ブロック」は、1行で実行できない場合に静的フィールドの値を設定するために使用されます。
しかし、そのために特別なブロックが必要な理由がわかりません。たとえば、フィールドを静的(値の割り当てなし)として宣言します。次に、上記で宣言した静的フィールドに値を生成して割り当てるコードの数行を記述します。
なぜ次のような特別なブロックにこの行が必要なのですstatic {...}
か?
私が理解している限り、「静的初期化ブロック」は、1行で実行できない場合に静的フィールドの値を設定するために使用されます。
しかし、そのために特別なブロックが必要な理由がわかりません。たとえば、フィールドを静的(値の割り当てなし)として宣言します。次に、上記で宣言した静的フィールドに値を生成して割り当てるコードの数行を記述します。
なぜ次のような特別なブロックにこの行が必要なのですstatic {...}
か?
回答:
非静的ブロック:
{
// 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
それらが静的初期化ブロックになかった場合、どこにありますか?初期化の目的でローカルであるだけの変数をどのように宣言し、フィールドと区別しますか?例えば、どのようになりますが書きたいです:
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;
}
}
ブロック内になかった場合first
、second
フィールドのように見えます。それらが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;
}
}
...しかし、同じブロック内に割り当てる複数の変数がある場合、または何もない場合(たとえば、何かをログに記録したい場合、またはネイティブライブラリを初期化する場合)は機能しません。
private static int widgets = 0; static{widgets = 2;}
private static int widgets = 0; static{widgets = 2;}
、「=」の割り当てが順番に行われることがわかりました。つまり、最初に置かれた「=」が最初に割り当てられます。上記の例では、「ウィジェット」の値は2になります(PSはコメントが5分でしか編集できないことを知りませんでした...)
次に例を示します。
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"が省略されていることを除いて、それらは静的初期化子ブロックと同じように見えます。
また、実行時に一部のクラスを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
。これとこの答えも見てください。
私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;
}
オブジェクトが静的ブロックで構築される前に、クラスに対してコードのビットを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());
}
}
静的ブロックは静的フィールドにしかアクセスできないと考えるのはよくある誤解です。このために、私が実際のプロジェクトでかなり頻繁に使用するコードの一部を以下に示します(少し異なるコンテキストで別の回答から部分的にコピーされます):
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
フィールドにもアクセスしますaliases
。static
ブロックは既にEnum
値のインスタンス(などENGLISH
)にアクセスできることを理解することが重要です。これは、ブロックが呼び出される前にフィールドがインスタンスで初期化されたかのように、タイプの場合の初期化と実行の順序Enum
が原因です。static private
static
Enum
暗黙の静的フィールドは定数。これには、Enumコンストラクターとインスタンスブロック、およびインスタンスの初期化も最初に発生する必要があります。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」を参照してください。
aliases
も、静的ブロックが非静的メンバーにアクセスできるわけではありません。/ static / メソッドによって返されaliases
たLanguage
値を介してアクセスされますvalues()
。あなたが言及したように、その時点で列挙変数がすでに利用可能であるという事実は異常なビットです-通常のクラスの非静的メンバーはこの状況ではアクセスできません。
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; } }
上記のコードは列挙型の例と同じで、静的ブロック内のインスタンス変数へのアクセスを許可しています
したがって、静的フィールド(クラスのインスタンスではなくクラスに属しているため、「クラス変数」とも呼ばれます。つまり、オブジェクトではなくクラスに関連付けられています)を初期化する必要があります。したがって、このクラスのインスタンスを作成せず、この静的フィールドを操作したい場合は、次の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
}
}
補足として、@ Pointyが言ったように
「静的」セクションのコードは、クラスのロード時に、クラスのインスタンスが構築される前(および静的メソッドが他の場所から呼び出される前)に実行されます。
System.loadLibrary("I_am_native_library")
静的ブロックに追加することになっています。
static{
System.loadLibrary("I_am_a_library");
}
これは、関連するライブラリがメモリにロードされる前にネイティブメソッドが呼び出されないことを保証します。
このメソッドが同じライブラリ名で複数回呼び出された場合、2番目以降の呼び出しは無視されます。
そのため、予期せぬことに、ライブラリが複数回ロードされるのを防ぐためにSystem.loadLibraryを使用することはありません。
最初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"をコンソールに出力します。クラスをインスタンス化していないことに注意してください。
静的ブロックは、動的な方法で静的データメンバーを初期化するためのテクノロジで使用されます。または、静的データメンバーの動的初期化で静的ブロックが使用されていると言えます。非静的データメンバーの初期化では、コンストラクターがあるため、静的データメンバーを動的に初期化できる場所
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はクラスのロード時にソリューションクラスと静的ブロックのロードをロードします。したがって、その静的データメンバーを動的に初期化できます。
}
{...}
vs の違いを知っていると思いましたstatic {...}
。(その場合、ジョンスキートは間違いなくあなたの質問により適切に答えました)