Javaの静的コンストラクターを完全に理解できませんでした。許可されている場合、なぜ許可されているのですか?どのシナリオで使用しますか?それはどのような目的に役立つでしょうか?誰か簡単な例を教えてもらえますか?
Javaの静的コンストラクターを完全に理解できませんでした。許可されている場合、なぜ許可されているのですか?どのシナリオで使用しますか?それはどのような目的に役立つでしょうか?誰か簡単な例を教えてもらえますか?
回答:
厳密に言えば、Javaには静的コンストラクターがありません。これは、コンストラクターが定義上静的ではないためです。参照しているものは、「静的初期化ブロック」と呼ばれます。コンストラクターは、オブジェクトを構築していることを意味します。クラスはそれ自体のインスタンスではないため、クラスのコンストラクターを持つことはできません。単なるクラスです。クラスを「構築」するものはコンパイラ(または「構築」の意味に応じて仮想マシン)と呼ばれ、他のコード内でコードを構築する場合は、コード生成に入ります。まったく異なる獣。
すべての細かい選択はさておき、静的初期化ブロックを使用して、クラスの複雑な静的(またはクラスレベル)フィールドを初期化します。通常、これらは、1行で初期化できないもの、または他のオブジェクト(静的ブロックが実装されているクラス内にある場合もそうでない場合もある)を最初に初期化する必要があるものを初期化するために使用されます。
基本的に、それらを使用して、クラスに「ねえ、変数Aを最初にこの値に設定し、それが完了したら、Aの値を使用してBを初期化する」ことができます。Javaでは、コンストラクタまたはメソッド内で、またはコンストラクタまたはメソッドの呼び出しを介して(リテラルでない場合)標準フィールドの初期化を行う必要があるため、これらは複雑な静的オブジェクトを初期化するための便利なメソッドになります。
静的初期化ブロックはあまり頻繁に必要ではないため、実際に使用する場合を除き、通常は避ける必要があります。誤解しないでください、彼らはJavaでの位置を持っていますが、他の多くのもの(break、return、switch、gotoステートメントなど)と同様に、簡単に使いすぎてしまう可能性があり、コードの可読性と保守性が低下します-baseで使用されます。
使用されている静的初期化ブロックの簡単な例は次のとおりです(ここにある静的初期化ブロックの優れた説明による)。
コード:
public class StaticExample{
static {
System.out.println("This is first static block");
}
public StaticExample(){
System.out.println("This is constructor");
}
public static String staticString = "Static Variable";
static {
System.out.println("This is second static block and "
+ staticString);
}
public static void main(String[] args){
StaticExample statEx = new StaticExample();
StaticExample.staticMethod2();
}
static {
staticMethod();
System.out.println("This is third static block");
}
public static void staticMethod() {
System.out.println("This is static method");
}
public static void staticMethod2() {
System.out.println("This is static method2");
}
}
出力:
This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2
静的ブロックが有用な場合にリストされるインスタンス:
(他の状況で)静的ブロックを使用しないいくつかの理由:
this
インスタンスがないため、キーワードを使用できません。注:一部の言語(C#など)には静的な「コンストラクター」の構文がありますが、これらの「コンストラクター」はJavaの静的初期化ブロックと同じように機能し、多くの(自分自身を含む)OOPコンストラクターの基本的な概念を考えると、言語の誤称です。
単純に割り当てるよりも難しいフィールドを初期化するために使用されます。
public class Example{
public final static Map<String, String> preFilledField;
static{
Map<String, String> tmp = new HashMap<>();
//fill map
preFilledField = Collections.unmodifiableMap(tmp);
}
}
初期化時にマップを埋めることはできません(匿名サブクラスハックを使用しない限り)。これは、最初に使用する前にマップが埋められることを保証する最良の方法です。
また、初期化時にチェック例外をキャッチするためにこれを行うことができます
Gurgadurgenの答えはおそらくあなたが探しているものですが、誰かが「静的コンストラクタ」を望んでいるときに無視されることがある他のいくつかのポイントを追加します。
クラスのインスタンスを作成する静的メソッドが必要な場合は、クラスのコンストラクターを単純に呼び出す静的メソッドを作成できます。
public class Example
{
/** Static method to create an instance. */
public static Example build()
{ return new Example() ; }
/** A field of each instance. */
private String stuff ;
/** The class's actual constructor. */
public Example()
{ stuff = new String() ; }
public String getStuff()
{ return this.stuff ; }
/**
* Mutator for "stuff" property. By convention this returns "void"
* but you might want to return the object itself, to support the
* sort of chained invocations that are becoming trendy now. You'll
* see the stylistic benefits of chaining later in this example.
*/
public Example setStuff( String newStuff )
{
this.stuff = newStuff ;
return this ;
}
}
public class ExampleTest
{
public static void main( String[] args )
{
// The usual instance model.
Example first = new Example() ;
System.out.println( first.setStuff("stuff").getStuff() ) ;
// Using your static method to construct an instance:
Example second = Example.build() ;
System.out.println( second.setStuff("more stuff").getStuff() ) ;
// Chaining all the invocations at once:
System.out.println( Example.build().setStuff("even more stuff").getStuff() ) ;
}
}
これにより、出力が生成されます。
stuff
more stuff
even more stuff
インスタンスを構築する静的メソッドを作成するもう1つの理由は、クラスのインスタンスが常に1つだけ存在することを確認したい場合です。これはシングルトンと呼ばれます。慣例により、このようなクラスは、「シングルトン」として扱われる唯一のインスタンスgetInstance()
を取得するために呼び出される静的メソッドを提供します。
public class SingletonExample extends Example
{
// Note: This extends my previous example, which has a "stuff"
// property, and a trivial constructor.
/** The singleton instance, statically initialized as null. */
private static SingletonExample singleton = null ;
/**
* The static accessor for the singleton. If no instance exists,
* then it will be created; otherwise, the one that already exists
* will be returned.
*/
public static SingletonExample getInstance()
{
if( singleton == null )
singleton = new SingletonExample() ;
return singleton ;
}
}
public class SingletonExampleTest
{
public static void main( String[] args )
{
System.out.println( SingletonExample.getInstance().setStuff("stuff").getStuff() ) ;
// You could still create instances of this class normally if you want to.
SingletonExample otherstuff = new SingletonExample() ;
otherstuff.setStuff("other stuff") ;
// But watch what happens to this.
System.out.println( SingletonExample.getInstance().getStuff() ) ;
System.out.println( otherstuff.getStuff() ) ;
// Now we show what happens when you start modifying the singleton.
SingletonExample theoneandonly = SingletonExample.getInstance() ;
theoneandonly.setStuff("changed stuff") ;
System.out.println( SingletonExample.getInstance().getStuff() ) ;
}
}
これにより、以下が生成されます。
stuff
stuff
other stuff
changed stuff
シングルトンへの参照を保存し、それを変更することにより、次の呼び出しgetInstance()
はその変更されたシングルトンを取得します。
アプリケーションのグローバル変数の作成を開始する方法としてシングルトンを使用するのは魅力的です。状況によっては、これは便利な場合もありますが、トラブルに巻き込まれる可能性もあります。特に、シングルトンインスタンスが失われる可能性のあるAndroidアプリの開発中に興味深いバグに遭遇しました。あるアクティビティから別のアクティビティにジャンプすると、以前のクラスローダーによって静的に保存されたシングルトンを認識しない新しい「クラスローダー」をJVMが使用する場合があります。
「静的コンストラクター」という概念を誤った名称と見なしている人が多いことは理解していますが、そうではないと思います。問題は、クラスとそのインスタンスオブジェクトの両方を構築するプロセスにあります。他のスレッドでは、クラスの構築はコンパイラの仕事であると述べられています。Javaでも、これは半分しか真実ではありません。
コンパイラーは、クラス定義ごとに足場を構築します。足場には、クラスに関するメタデータと、構築時にインスタンスに含まれるべきインスタンスが含まれます。クラスが定数プリミティブ値を割り当てられたフィールドを定義する場合、その値はコンパイラーによってスキャフォールドに含まれます。割り当てられている他の値タイプの場合、コンパイラは、最初のクラスインスタンスの作成前に1回実行される初期化ルーチンを生成し、適切な値でスキャフォールドを更新します。この更新は、コンパイラーによって実行できません。
足場に対するこの1回限りの更新は、多くの場合、インスタンスコンストラクターが適切に機能するための重要な前提条件であるため、一種のコンストラクターと呼ぶことも合理的です。これが、この概念をサポートする一般的なオブジェクト指向言語では、静的コンストラクターと呼ばれる理由です。Javaの静的初期化ブロックの背後にある概念は、Javaプログラマーがシステムと実装に依存しないという概念に沿った意味の変更にすぎません。
他の答えが言ったように、オブジェクトを構築する静的メソッドを書くことができます。これは、Java(および他の多くの言語。Delphiは名前付きコンストラクターをサポートしています)の名前付きコンストラクターの不足を回避します。パラメーターのタイプと順序を試してみることはできますが、これによりコードが不明瞭で脆弱になる可能性があります。
たとえば、オブジェクトをXML文字列またはJSON文字列から構築できるシナリオを発明できます。次のようなメソッドを書くことができます。
static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);
名前付き初期化メソッドを使用したパラメーターなしのコンストラクターの代替として、非常にまれにこれを使用しました。
MyObject myObject = new MyObject();
myObject.loadXml(xml).
静的createメソッドは、クラス内でビルダーパターンを実装していると見なすことができます。
クラスプロパティを初期化するために使用するクラスレベルコンストラクターのような「静的」セクションを表示できます(Javaでは静的)。インスタンスレベルのプロパティを初期化するために使用される「通常の」コンストラクタと同じです。