java static - Bloques de inicialización estática




member class (12)

Por lo que he entendido, el "bloque de inicialización estática" se utiliza para establecer valores de campo estático si no se puede hacer en una línea.

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

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


Answers

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

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

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

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


Entonces, tiene 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, se asocia a la clase en lugar de a cualquier objeto) y desea inicializarla. Entonces, 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 lo inicialice: esta es la alternativa al bloque estático anterior; Puedes escribir un método estático privado:

public static int x=initializeX();

private static int initializeX(){
 return 3;
}

Ahora, ¿por qué usarías bloque de inicialización estática en lugar de métodos estáticos?

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

Digamos que tienes una matriz compleja en tu programa. Lo inicializa (utilizando for loop por ejemplo) y luego los valores de esta matriz cambiarán a lo largo del programa, pero luego, en algún momento, desea reinicializarlo (volver al valor inicial). En este caso puedes llamar al método estático privado. En caso de que no necesite en su programa para reinicializar los valores, puede usar el bloque estático y no necesita un método estático ya que no lo usará más adelante en el programa.

Nota: los bloques estáticos se llaman 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(A.a); // 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(A.a); // this will print 3
 }

}

Como complemento, como dijo @Pointy.

El código en la sección (es) "estática" se ejecutará en el momento de carga de la clase, antes de que se construya cualquier instancia de la clase (y antes de que se llame a cualquier método estático).

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.

Según loadLibrary desde oracle :

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

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


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

Para reutilizar algunos ejemplos publicados aquí.

Este fragmento de código podría reescribirse sin utilizar un inicializador static .

Método # 1: Con static

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

Método # 2: Sin 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;
}

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 uso a menudo 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<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); 
  } 
} 

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 integrado valueOf proporcionado por el propio Enum .

Como puede ver, el inicializador estático accede incluso a los aliases campos private . Es importante entender que el bloque static ya tiene acceso a las instancias de valor Enum (por ejemplo, ENGLISH ). Esto se debe a que el 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 llamar a los bloques static :

  1. Las constantes 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. Bloqueo static e inicialización de campos estáticos en el orden de ocurrencia.

Es importante tener en cuenta esta inicialización fuera de orden (constructor antes del bloque static ). También sucede 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 suceder antes del constructor, e incluso después:

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, nuevamente llamamos a los constructores para los campos estáticos, después de lo cual se reanuda la inicialización estática, y completa el constructor llamado desde dentro del método principal. Situación bastante compleja para la que espero que en la codificación normal no tengamos que lidiar.

Para más información sobre esto, vea el libro " Java efectiva ".


Primero debe comprender que las clases de su aplicación están java.class.Class objetos java.class.Class durante el tiempo de ejecución. Esto es cuando se ejecutan sus bloques estáticos. Así que en realidad 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" a la consola. Tenga en cuenta que no he instanciado ninguna clase.


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. Imagina que getStuff() lanza 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 utiliza el inicializador static para registrarse en el DriverManager . También vea this y this respuesta.


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

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

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


Aquí hay un ejemplo:

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

El código en la sección (es) "estática" se ejecutará en el momento de carga de la clase, antes de que se construya cualquier instancia de la clase (y antes de que se llame a cualquier método estático). De esa manera, puede asegurarse de que los recursos de la clase estén listos para usar.

También es posible tener bloques de inicialización no estáticos. Esos actúan como extensiones al conjunto de métodos constructores definidos para la clase. Se ven como bloques de inicialización estáticos, excepto que la palabra clave "estática" se deja.


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 del miembro de datos estáticos se está utilizando el bloque estático ... Porque para la inicialización de miembros de datos no estáticos tenemos un 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 x estático se inicializará dinámicamente ..Bcoz cuando el compilador irá a Solution.x cargará la clase de solución y la carga del bloque estático en el momento de carga de la clase ... Así podremos inicializar dinámicamente ese miembro de datos estáticos ...

}


Cuando declaramos una clase miembro estática dentro de una clase, se conoce como clase anidada de nivel superior o una clase anidada estática. Se puede demostrar de la siguiente manera:

class Test{
    private static int x = 1;
        static class A{
        private static int y = 2;
        public static int getZ(){
            return B.z+x;
        }
    }
    static class B{
        private static int z = 3;
        public static int getY(){
            return A.y;
        }
    }
}

class TestDemo{
     public static void main(String[] args){
        Test t = new Test();
        System.out.println(Test.A.getZ());
        System.out.println(Test.B.getY());
    }
}

Cuando declaramos una clase miembro no estática dentro de una clase, se conoce como clase interna. La clase interna se puede demostrar de la siguiente manera:

    class Test{
        private int i = 10;
        class A{
            private int i =20;
            void display(){
            int i = 30;
            System.out.println(i);
            System.out.println(this.i);
            System.out.println(Test.this.i);
        }
    }
}






java static initialization static-block initialization-block