wake-up-neo.com

Statische Initialisierungsblöcke

Soweit ich verstanden habe, wird der "statische Initialisierungsblock" verwendet, um Werte des statischen Feldes festzulegen, wenn dies nicht in einer Zeile möglich ist.

Aber ich verstehe nicht, warum wir dafür einen speziellen Block brauchen. Zum Beispiel deklarieren wir ein Feld als statisch (ohne Wertzuweisung). Schreiben Sie dann mehrere Codezeilen, die das oben deklarierte statische Feld erzeugen und ihm einen Wert zuweisen.

Warum brauchen wir diese Zeilen in einem speziellen Block wie: static {...}?

246
Roman

Der nicht statische Block:

{
    // Do Something...
}

Wird aufgerufen jedes Mal Eine Instanz der Klasse wird erstellt. Der statische Block wird nur einmal aufgerufen, wenn die Klasse selbst initialisiert wird, unabhängig davon, wie viele Objekte dieses Typs Sie erstellen .

Beispiel:

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();
    }
}

Dies druckt:

Static
Non-static block
Non-static block
402

Wenn sie nicht in einem statischen Initialisierungsblock wären, wo wären sie dann? Wie würden Sie eine Variable deklarieren, die nur zum Zwecke der Initialisierung lokal sein sollte, und sie von einem Feld unterscheiden? Zum Beispiel, wie würden Sie schreiben wollen :

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;
    }
}

Wenn first und second nicht in einem Block wären, würden sie wie Felder aussehen. Wenn sie sich in einem Block ohne static befänden, würde dies als Instanzinitialisierungsblock anstelle eines statischen Initialisierungsblocks gezählt, sodass sie einmal ausgeführt würde pro konstruierte Instanz als einmal insgesamt.

In diesem speziellen Fall können Sie stattdessen eine statische Methode verwenden:

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;
    }
}

... aber das funktioniert nicht, wenn es mehrere Variablen gibt, die Sie innerhalb desselben Blocks zuweisen möchten, oder keine (z. B. wenn Sie nur etwas protokollieren möchten - oder möglicherweise eine native Bibliothek initialisieren möchten).

124
Jon Skeet

Hier ist ein Beispiel:

  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");
  }

Der Code in den Abschnitten "static" wird zum Zeitpunkt des Ladens der Klasse ausgeführt, bevor Instanzen der Klasse erstellt werden (und bevor statische Methoden von einer anderen Stelle aufgerufen werden). Auf diese Weise können Sie sicherstellen, dass alle Klassenressourcen einsatzbereit sind.

Es ist auch möglich, nicht statische Initialisierungsblöcke zu haben. Diese verhalten sich wie Erweiterungen der Konstruktormethoden, die für die Klasse definiert wurden. Sie sehen genauso aus wie statische Initialisierungsblöcke, außer dass das Schlüsselwort "static" weggelassen wird.

98
Pointy

Dies ist auch nützlich, wenn Sie den Wert eigentlich keinem Wert zuweisen möchten, z. B. wenn Sie eine Klasse nur einmal zur Laufzeit laden möchten.

Z.B.

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

Hey, es gibt noch einen weiteren Vorteil: Sie können damit Ausnahmen behandeln. Stellen Sie sich vor, dass getStuff() hier ein Exception wirft, das wirklich in einen catch-Block gehört:

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

dann ist hier ein staticInitialisierer nützlich. Dort können Sie die Ausnahme behandeln.

Ein weiteres Beispiel ist das Ausführen von Aufgaben, die während der Zuweisung nicht ausgeführt werden können:

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);
    }
}

Um auf das JDBC-Treiberbeispiel zurückzukommen, verwendet jeder anständige JDBC-Treiber selbst auch den Initialisierer static, um sich in DriverManager zu registrieren. Siehe auch this und this Antwort.

47
BalusC

Ich würde sagen static block ist nur syntaktischer Zucker. Es gibt nichts, was Sie mit dem static -Block tun könnten, und nichts anderes.

Um einige hier veröffentlichte Beispiele wiederzuverwenden.

Dieser Code kann ohne Verwendung des Initialisierers static neu geschrieben werden.

Methode 1: Mit static

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

Methode 2: Ohne 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;
}
11
user1508893

Es gibt einige tatsächliche Gründe, aus denen es erforderlich ist, zu existieren:

  1. initialisierung von static final Mitglieder, deren Initialisierung möglicherweise eine Ausnahme auslöst
  2. initialisierung von static final Mitglieder mit berechneten Werten

Die Leute neigen dazu, static {} -Blöcke sind eine bequeme Methode, um Dinge zu initialisieren, von denen die Klasse auch innerhalb der Laufzeit abhängt - beispielsweise um sicherzustellen, dass eine bestimmte Klasse geladen ist (z. B. JDBC-Treiber). Das kann auf andere Weise geschehen; Die beiden Dinge, die ich oben erwähne, können jedoch nur mit einem Konstrukt wie dem static {} Block.

10
D.Shawley

Sie können Codebits einmal für eine Klasse ausführen, bevor ein Objekt in den statischen Blöcken erstellt wird.

Z.B.

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());
  }
}

Es ist ein weit verbreitetes Missverständnis, dass ein statischer Block nur Zugriff auf statische Felder hat. Dazu möchte ich im Folgenden einen Code zeigen, den ich häufig in realen Projekten verwende (teilweise kopiert aus eine andere Antwort in einem etwas anderen Kontext):

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); 
  } 
} 

Hier wird der Initialisierer verwendet, um einen Index (ALIAS_MAP), um eine Reihe von Aliasnamen dem ursprünglichen Aufzählungstyp zuzuordnen. Es ist als Erweiterung der integrierten valueOf-Methode gedacht, die von Enum selbst bereitgestellt wird.

Wie Sie sehen, greift der statische Initialisierer sogar auf das Feld privatealiases zu. Es ist wichtig zu verstehen, dass der Block static bereits Zugriff auf die Enum - Wertinstanzen hat (z. B. ENGLISH). Dies liegt daran, dass die Reihenfolge der Initialisierung und Ausführung im Fall von Enum -Typen ist, als ob die static private Felder wurden mit Instanzen initialisiert, bevor die static Blöcke aufgerufen wurden:

  1. Die Konstanten Enum, die implizite statische Felder sind. Dazu müssen zuerst der Enum-Konstruktor und die Instanzblöcke sowie die Instanzinitialisierung ausgeführt werden.
  2. static Block und Initialisierung statischer Felder in der Reihenfolge ihres Auftretens.

Diese Initialisierung außerhalb der Reihenfolge (Konstruktor vor dem Block static) ist wichtig. Es passiert auch, wenn wir statische Felder mit Instanzen ähnlich einem Singleton initialisieren (Vereinfachungen vorgenommen):

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();
  }
}

Was wir sehen, ist die folgende Ausgabe:

Static Block 1
Constructor
Static Block 2
In Main
Constructor

Es ist klar, dass die statische Initialisierung tatsächlich vor dem Konstruktor und sogar danach erfolgen kann:

Wenn Sie in der main-Methode einfach auf Foo zugreifen, wird die Klasse geladen und die statische Initialisierung gestartet. Im Rahmen der statischen Initialisierung rufen wir jedoch erneut die Konstruktoren für die statischen Felder auf. Danach wird die statische Initialisierung fortgesetzt und der Konstruktor wird innerhalb der Hauptmethode aufgerufen. Ziemlich komplexe Situation, mit der ich hoffe, dass wir uns bei normaler Codierung nicht befassen müssen.

Weitere Informationen hierzu finden Sie im Buch " Effektives Java ".

7
YoYo

Wenn Ihre statischen Variablen zur Laufzeit gesetzt werden müssen, wird ein static {...} Block ist sehr hilfreich.

Zum Beispiel, wenn Sie das statische Element auf einen Wert setzen müssen, der in einer Konfigurationsdatei oder Datenbank gespeichert ist.

Dies ist auch nützlich, wenn Sie einem statischen Member Map Werte hinzufügen möchten, da Sie diese Werte nicht in der ursprünglichen Memberdeklaration hinzufügen können.

3
Marcus Leon

Sie haben also ein statisches Feld (es wird auch als "Klassenvariable" bezeichnet, da es eher zur Klasse als zu einer Instanz der Klasse gehört; mit anderen Worten, es ist eher mit der Klasse als mit einem Objekt verknüpft) und Sie möchten es initialisieren. Wenn Sie also KEINE Instanz dieser Klasse erstellen und dieses statische Feld bearbeiten möchten, haben Sie drei Möglichkeiten:

1- Initialisieren Sie es einfach, wenn Sie die Variable deklarieren:

static int x = 3;

2- Einen statischen Initialisierungsblock haben:

static int x;

static {
 x=3;
}

3- Haben Sie eine Klassenmethode (statische Methode), die auf die Klassenvariable zugreift und diese initialisiert: Dies ist die Alternative zum obigen statischen Block; Sie können eine private statische Methode schreiben:

public static int x=initializeX();

private static int initializeX(){
 return 3;
}

Warum sollten Sie nun den statischen Initialisierungsblock anstelle von statischen Methoden verwenden?

Es hängt wirklich davon ab, was Sie in Ihrem Programm benötigen. Sie müssen jedoch wissen, dass der statische Initialisierungsblock einmal aufgerufen wird. Der einzige Vorteil der Klassenmethode besteht darin, dass sie später wiederverwendet werden können, wenn Sie die Klassenvariable neu initialisieren müssen.

nehmen wir an, Sie haben ein komplexes Array in Ihrem Programm. Sie initialisieren es (zum Beispiel mit for loop) und dann ändern sich die Werte in diesem Array im gesamten Programm, aber Sie möchten es irgendwann erneut initialisieren (kehren zum ursprünglichen Wert zurück). In diesem Fall können Sie die private statische Methode aufrufen. Falls Sie die Werte in Ihrem Programm nicht neu initialisieren müssen, können Sie einfach den statischen Block verwenden und benötigen keine statische Methode, da Sie sie später im Programm nicht verwenden werden.

Hinweis: Die statischen Blöcke werden in der Reihenfolge aufgerufen, in der sie im Code erscheinen.

Beispiel 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
 }

}

Beispiel 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
 }

}
2
Randa Sbeity

Sie müssen zuerst verstehen, dass Ihre Anwendungsklassen selbst instanziiert sind zu Java.class.Class Objekte zur Laufzeit. Dies ist, wenn Ihre statischen Blöcke ausgeführt werden. Sie können dies also tatsächlich tun:

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) {
    }

}

und es würde "myInt ist 1" zur Konsole drucken. Beachten Sie, dass ich keine Klasse instanziiert habe.

0
eosimosu

Als Ergänzung, wie @Pointy sagte

Der Code in den Abschnitten "static" wird zum Zeitpunkt des Ladens der Klasse ausgeführt, bevor Instanzen der Klasse erstellt werden (und bevor statische Methoden von einer anderen Stelle aufgerufen werden).

Es soll System.loadLibrary("I_am_native_library") in statischen Block einfügen.

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

Es wird garantiert, dass keine native Methode aufgerufen wird, bevor die zugehörige Bibliothek in den Speicher geladen wurde.

Laut loadLibrary von Oracle :

Wenn diese Methode mehrmals mit demselben Bibliotheksnamen aufgerufen wird, werden der zweite und die nachfolgenden Aufrufe ignoriert.

Aus diesem Grund wird das Setzen von System.loadLibrary nicht verwendet, um zu vermeiden, dass die Bibliothek mehrmals geladen wird.

0
Gearon
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");
    } 
}
0
Vid