私が理解している限り、「静的初期化ブロック」は、1行で実行できない場合に静的フィールドの値を設定するために使用されます。
しかし、そのために特別なブロックが必要な理由がわかりません。たとえば、フィールドを静的 (値の割り当てなし) として宣言します。次に、上記で宣言された静的フィールドに値を生成して割り当てるコードを数行記述します。
のような特別なブロックでこの行が必要なのはなぜstatic {...}
ですか?
私が理解している限り、「静的初期化ブロック」は、1行で実行できない場合に静的フィールドの値を設定するために使用されます。
しかし、そのために特別なブロックが必要な理由がわかりません。たとえば、フィールドを静的 (値の割り当てなし) として宣言します。次に、上記で宣言された静的フィールドに値を生成して割り当てるコードを数行記述します。
のような特別なブロックでこの行が必要なのはなぜstatic {...}
ですか?
非静的ブロック:
{
// Do Something...
}
クラスのインスタンスが構築されるたびに呼び出されます。静的ブロックは、作成するそのタイプのオブジェクトの数に関係なく、クラス自体が初期化されるときに1 回だけ呼び出されます。
例:
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 final HashMap<String, String> MAP = new HashMap<String, String>();
static {
MAP.put("banana", "honey");
MAP.put("peanut butter", "jelly");
MAP.put("rice", "beans");
}
「静的」セクションのコードは、クラスのロード時に、クラスのインスタンスが構築される前 (および静的メソッドが他の場所から呼び出される前) に実行されます。こうすることで、クラスのリソースをすべて使用する準備ができていることを確認できます。
非静的初期化ブロックを持つことも可能です。これらは、クラスに定義された一連のコンストラクター メソッドに対する拡張機能のように機能します。これらは、キーワード「static」が省略されていることを除いて、静的初期化ブロックと同じように見えます。
また、実行時にクラスを一度だけロードするなど、実際には値を何にも割り当てたくない場合にも役立ちます。
例えば
static {
try {
Class.forName("com.example.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError("Cannot load JDBC driver.", e);
}
}
ねえ、別の利点があります。それを使用して例外を処理できます。ここで実際に catch ブロックに属する をgetStuff()
スローすると想像してください。Exception
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
. thisとthis answerも参照してください。
私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;
}
存在する必要があるいくつかの実際の理由があります。
static final
で例外がスローされる可能性があるメンバーの初期化static final
計算値によるメンバーの初期化static {}
特定のクラス (JDBC ドライバーなど) が確実に読み込まれるようにするなど、ランタイム内でもクラスが依存するものを初期化するための便利な方法として、ブロックを使用する傾向があります。それは他の方法で行うことができます。ただし、上で述べた 2 つのことは、static {}
ブロックのような構成でのみ実行できます。
静的ブロックでオブジェクトが構築される前に、クラスに対してコードのビットを 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
)。これは、ブロックが呼び出される前にフィールドがインスタンスで初期化されたかのように、typesの場合の初期化と実行の順序によるものです。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」を参照してください。
したがって、静的フィールドがあり (クラスのインスタンスではなくクラスに属しているため、「クラス変数」とも呼ばれます。つまり、オブジェクトではなくクラスに関連付けられています)、それを初期化します。したがって、このクラスのインスタンスを作成したくないが、この静的フィールドを操作したい場合は、次の 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
}
}
実行時に静的変数を設定する必要がある場合は、static {...}
ブロックが非常に役立ちます。
たとえば、構成ファイルまたはデータベースに格納されている値に静的メンバーを設定する必要がある場合。
Map
初期メンバー宣言ではこれらの値を追加できないため、静的メンバーに値を追加する場合にも役立ちます。
静的ブロックは、動的な方法で静的データメンバーを初期化するためにあらゆるテクノロジーに使用されます。または、静的データメンバーの動的初期化のために静的ブロックが使用されていると言えます..非静的データメンバーの初期化にはコンストラクターがありますが、ないためです静的データメンバーを動的に初期化できる場所
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 は動的に初期化されます..Bcoz コンパイラが Solution.x に移動すると、ソリューション クラスが読み込まれ、クラスの読み込み時に静的ブロックが読み込まれます..したがって、その静的データ メンバーを動的に初期化できます..
}