Bloques de inicialización estática

Por lo que entiendo, el “bloque de inicialización estática” se usa para establecer valores de campo estático si no se puede hacer en una línea.

Pero no entiendo por qué necesitamos un bloque especial para eso. Por ejemplo, declaramos un campo como estático (sin una asignación de valores). Y luego escriba varias líneas del código que generan y asignan un valor al campo estático declarado arriba.

¿Por qué necesitamos estas líneas en un bloque especial como: static {...} ?

El bloque no estático:

 { // Do Something... } 

Se llama cada vez que se construye una instancia de la clase. El bloque estático solo se llama una vez , cuando la clase misma se inicializa, no importa cuántos objetos de ese tipo cree.

Ejemplo:

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

Esto imprime:

 Static Non-static block Non-static block 

Si no estuvieran en un bloque de inicialización estático, ¿dónde estarían? ¿Cómo declararía una variable que solo pretendía ser local para los fines de la inicialización y la distinguiría de un campo? Por ejemplo, cómo le gustaría escribir:

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

Si el first y el second no estaban en un bloque, se verían como campos. Si estuvieran en un bloque sin static frente a él, eso se contaría como un bloque de inicialización de instancia en lugar de un bloque de inicialización estático, por lo que se ejecutaría una vez por instancia construida en lugar de una vez en total.

Ahora, en este caso particular, podrías usar un método estático en su lugar:

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

… pero eso no funciona cuando hay múltiples variables que desea asignar dentro del mismo bloque, o ninguna (por ejemplo, si solo desea registrar algo, o tal vez inicializar una biblioteca nativa).

Aquí hay un ejemplo:

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

El código en la (s) sección (es) “estática” se ejecutará en el tiempo de carga de la clase, antes de construir cualquier instancia de la clase (y antes de llamar a cualquier método estático de otro lugar). De esta forma, puede asegurarse de que los recursos de la clase estén listos para usar.

También es posible tener bloques de inicializador no estáticos. Esos actúan como extensiones del conjunto de métodos constructor definidos para la clase. Se ven como bloques de inicializadores estáticos, excepto que la palabra clave “estática” se deja desactivada.

También es útil cuando en realidad no desea asignar el valor a nada, como cargar alguna clase solo una vez durante el tiempo de ejecución.

P.ej

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

Oye, hay otro beneficio, puedes usarlo para manejar excepciones. Imagine que getStuff() aquí arroja una Exception que realmente pertenece a un bloque catch:

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

entonces un inicializador static es útil aquí. Puede manejar la excepción allí.

Otro ejemplo es hacer cosas después que no se pueden hacer durante la asignación:

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

Para volver al ejemplo del controlador JDBC, cualquier controlador JDBC decente también hace uso del inicializador static para registrarse en el DriverManager . También vea esto y esta respuesta.

Yo diría que static block es solo azúcar sintáctico. No hay nada que puedas hacer con static locking static y no con nada más.

Para reutilizar algunos ejemplos publicados aquí.

Este fragmento de código podría volver a escribirse sin usar static inicializador static .

Método n. ° 1: con static

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

Método n. ° 2: sin static

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

Hay algunas razones reales por las que se requiere que exista:

  1. inicializar miembros static final cuya inicialización podría arrojar una excepción
  2. inicializando miembros static final con valores calculados

Las personas tienden a usar bloques static {} como una forma conveniente de inicializar cosas de las que la clase también depende dentro del tiempo de ejecución, como asegurar que se cargue una clase en particular (por ejemplo, controladores JDBC). Eso se puede hacer de otras maneras; sin embargo, las dos cosas que menciono arriba solo se pueden hacer con un constructo como el bloque static {} .

Puede ejecutar bits de código una vez para una clase antes de que se construya un objeto en los bloques estáticos.

P.ej

 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 un error común pensar que un bloque estático solo tiene acceso a campos estáticos. Para esto, me gustaría mostrar a continuación un fragmento de código que muy a menudo uso en proyectos de la vida real (copiado parcialmente de otra respuesta en un contexto ligeramente diferente):

 public enum Language { ENGLISH("eng", "en", "en_GB", "en_US"), GERMAN("de", "ge"), CROATIAN("hr", "cro"), RUSSIAN("ru"), BELGIAN("be",";-)"); static final private Map ALIAS_MAP = new HashMap(); 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 aliases; private Language(String... aliases) { this.aliases = Arrays.asList(aliases); } } 

Aquí, el inicializador se usa para mantener un índice ( ALIAS_MAP ), para asignar un conjunto de alias al tipo de enumeración original. Está pensado como una extensión del método valueOf incorporado provisto por el propio Enum .

Como puede ver, el inicializador estático accede incluso a los aliases campo private . Es importante comprender que el bloque static ya tiene acceso a las instancias del valor Enum (por ejemplo, ENGLISH ). Esto se debe al orden de inicialización y ejecución en el caso de los tipos Enum , como si los campos static private se hubieran inicializado con instancias antes de que se hayan llamado los bloques static :

  1. Las constantes de Enum que son campos estáticos implícitos. Esto requiere que el constructor Enum y los bloques de instancia, y que la inicialización de la instancia también ocurra primero.
  2. bloque static e inicialización de campos estáticos en el orden de aparición.

Esta inicialización fuera de orden (constructor antes del bloque static ) es importante tener en cuenta. También ocurre cuando inicializamos campos estáticos con las instancias de manera similar a un Singleton (simplificaciones hechas):

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

Lo que vemos es el siguiente resultado:

 Static Block 1 Constructor Static Block 2 In Main Constructor 

Claro es que la inicialización estática en realidad puede ocurrir antes del constructor, e incluso después de:

Simplemente accediendo a Foo en el método principal, hace que la clase se cargue y se inicie la inicialización estática. Pero como parte de la inicialización Estática, llamamos nuevamente a los constructores para los campos estáticos, luego de lo cual se reanuda la inicialización estática y se completa el constructor llamado desde el método principal. Situación bastante compleja para la que espero que en la encoding normal no tengamos que tratar.

Para obtener más información al respecto, consulte el libro ” Java efectivo “.

Si sus variables estáticas deben establecerse en tiempo de ejecución, un bloque static {...} es muy útil.

Por ejemplo, si necesita establecer el miembro estático en un valor que está almacenado en un archivo de configuración o base de datos.

También es útil cuando desea agregar valores a un miembro de Map estático ya que no puede agregar estos valores en la statement de miembro inicial.

Entonces tienes un campo estático (también se llama “variable de clase” porque pertenece a la clase en lugar de a una instancia de la clase, en otras palabras, está asociado con la clase en lugar de con cualquier objeto) y quieres inicializarlo. Por lo tanto, si NO desea crear una instancia de esta clase y desea manipular este campo estático, puede hacerlo de tres maneras:

1- Simplemente inicialízalo cuando declares la variable:

 static int x = 3; 

2- Tener un bloque de inicialización estático:

 static int x; static { x=3; } 

3- Tener un método de clase (método estático) que acceda a la variable de clase y la inicialice: esta es la alternativa al bloque estático anterior; puedes escribir un método privado estático:

 public static int x=initializeX(); private static int initializeX(){ return 3; } 

Ahora, ¿por qué usaría el bloque de inicialización estático en lugar de los métodos estáticos?

Realmente depende de lo que necesita en su progtwig. Pero debe saber que el bloque de inicialización estático se llama una vez y que la única ventaja del método de clase es que pueden reutilizarse más adelante si necesita reinicializar la variable de clase.

digamos que tienes una matriz compleja en tu progtwig. Inicializarlo (usando for loop por ejemplo) y luego los valores en este conjunto cambiarán a lo largo del progtwig, pero luego, en algún punto, querrá reinicializarlo (volver al valor inicial). En este caso, puede llamar al método estático privado. En caso de que no necesite en su progtwig reinicializar los valores, puede usar el bloque estático y no necesita un método estático ya que no lo va a utilizar más adelante en el progtwig.

Nota: los bloques estáticos se invocan en el orden en que aparecen en el código.

Ejemplo 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(Aa); // this will print 5 } } 

Ejemplo 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(Aa); // this will print 3 } } 

Como complementario, como dijo @Pointy

El código en la (s) sección (es) “estática” se ejecutará en el tiempo de carga de la clase, antes de construir cualquier instancia de la clase (y antes de llamar a cualquier método estático de otro lugar).

Se supone que debe agregar System.loadLibrary("I_am_native_library") en el bloque estático.

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

Garantizará que no se llame a ningún método nativo antes de que la biblioteca relacionada se cargue en la memoria.

De acuerdo con loadLibrary de Oracle :

Si se llama a este método más de una vez con el mismo nombre de biblioteca, la segunda llamada y las siguientes se ignorarán.

Entonces, de manera bastante inesperada, poner System.loadLibrary no se usa para evitar que la biblioteca se cargue varias veces.

Primero necesita comprender que sus clases de aplicación se instancian a los objetos java.class.Class durante el tiempo de ejecución. Aquí es cuando se ejecutan sus bloques estáticos. Entonces puedes hacer esto:

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

e imprimiría “myInt is 1” para consola. Tenga en cuenta que no he instanciado ninguna clase.

Los bloques estáticos se ejecutan solo una vez en el momento de la carga e inicialización de clase por parte de JVM, es decir, la primera vez que se hace referencia a la clase en el código.

Un ejemplo típico de uso de un bloque estático es cuando admite la recuperación de una instancia de Enum por su valor, para ello define un hashmap como una variable estática que asigna cada valor a su instancia de Enum correspondiente, el mapa se inicializa y se llena dentro de un estático bloquear antes de utilizar Enum en la aplicación.

 public enum ErrorCodes { BUSINESS_ERROR(100), SERVER_ERROR(500), NETWORK_ERROR(1000); private int errorCode; // This field maps each error code numeric value to a corresponding Enum instance. private static Map errorCodeByErrorNumber = new HashMap(); static { for (ErrorCodes errorCode : ErrorCodes.values()) { errorCodeByErrorNumber.put(errorCode.getErrorCode(), errorCode); } } private ErrorCodes(int errorCode) { this.errorCode = errorCode; } public int getErrorCode() { return errorCode; } public static ErrorCodes getErrorCodeByNumber(Integer dayNumber) { return errorCodeByErrorNumber.get(dayNumber); } } 

Referencia: palabra clave estática en java

el bloque estático se usa para cualquier tecnología para inicializar el miembro de datos estáticos de forma dinámica, o podemos decir que para la inicialización dinámica de los miembros estáticos se está utilizando el bloque estático … Debido a la inicialización de los miembros de datos no estáticos, tenemos el constructor pero no tenemos cualquier lugar donde podamos inicializar dinámicamente el miembro de datos estáticos

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

Ahora mi int int estática se inicializará dinámicamente ..Bcoz cuando el comstackdor vaya a Solution.x cargará la clase de solución y la carga de bloque estático en el tiempo de carga de clases … Así podremos inicializar dinámicamente ese miembro de datos estáticos …

}