Necesidad de bloque estático en Java

10 minutos de lectura

avatar de usuario
sexybestia

Encontré que en Java, hay una función llamada static block, que incluye código que se ejecuta cuando una clase se carga por primera vez (no entiendo qué significa ‘cargado’, ¿significa inicializado?). ¿Hay alguna razón para hacer el bit de inicialización dentro de un bloque estático y no en el constructor? Quiero decir, incluso el constructor hace lo mismo, hace todo lo necesario cuando se inicializa una clase por primera vez. ¿Hay algo que el bloque estático logre que un constructor no pueda?

  • Llamado una vez cuando el cargador de clases carga la clase. No cada vez que se crea una instancia de la clase.

    – Wolfgang Kuehn

    21 dic 2012 a las 18:00

  • Por favor, eche un vistazo a los siguientes capítulos del Tutorial Oficial de Java (de Oracle): Comprensión de los miembros de instancia y de clase y Inicializar campos. Vuelve si aún tienes dudas.

    –Anthony Accioly

    21 de diciembre de 2012 a las 18:02


  • Solo una pregunta sin sentido, si una clase tiene ambos main y un constructor, ¿cuál se invoca antes cuando se crea una instancia de la clase?

    – Bestia Sexy

    21 de diciembre de 2012 a las 18:10

  • Código de inicialización (Tiempo de carga de clase) -> Principal (Punto de entrada de la aplicación) -> Constructores: aquí hay un ejemplo de trabajo para ilustrar mejor el punto.

    –Anthony Accioly

    21 de diciembre de 2012 a las 18:27


  • @Cupidvogel El main El método no se ejecuta cada vez que se crea una instancia de una clase. Solo se ejecuta cuando se invoca explícitamente (como cualquier otro método estático) o cuando la JVM inicia la aplicación.

    – Espina G

    21 de diciembre de 2012 a las 19:42

avatar de usuario
ted salto

Primero quiero resaltar una cosa de tu pregunta:

el constructor hace lo mismo, hace todo lo necesario cuando una clase se inicializa por primera vez

Esto es incorrecto. Un constructor hace toda la inicialización necesaria cuando un instancia de una clase se crea. Ningún constructor se ejecuta cuando la clase en sí se carga por primera vez en la memoria y se inicializa (a menos que se cree una instancia de la clase como parte de la inicialización de la clase). Esta confusión (entre inicializar una clase e inicializar instancias de la clase) es probablemente la razón por la que cuestiona la utilidad de static bloques

Si una clase tiene miembros estáticos que requieren inicialización compleja, un static block es la herramienta a utilizar. Suponga que necesita un mapa estático de algún tipo (el propósito es irrelevante aquí). Puedes declararlo en línea así:

public static final Map<String, String> initials = new HashMap<String, String>();

Sin embargo, si desea completarlo una vez, no puede hacerlo con una declaración en línea. Para eso, necesitas un static bloquear:

public static final Map<String, String> initials = new HashMap<String, String>();
static {
    initials.put("AEN", "Alfred E. Newman");
    // etc.
}

Si desea ser aún más protector, puede hacer esto:

public static final Map<String, String> initials;
static {
    Map<String, String> map = new HashMap<String, String>()
    map.put("AEN", "Alfred E. Newman");
    // etc.
    initials = Collections.unmodifiableMap(map);
}

Tenga en cuenta que no puede inicializar initials en línea como un mapa no modificable porque entonces no podría poblarlo. Tampoco puede hacer esto en un constructor porque simplemente llamar a uno de los métodos de modificación (putetc.) generará una excepción.

Para ser justos, esta no es una respuesta completa a su pregunta. los static el bloque aún podría eliminarse mediante el uso de una función estática privada:

public static final Map<String, String> initials = makeInitials();

private static Map<String, String> makeInitials() {
    Map<String, String> map = new HashMap<String, String>()
    map.put("AEN", "Alfred E. Newman");
    // etc.
    return Collections.unmodifiableMap(map);
}

Tenga en cuenta, sin embargo, que esto no está reemplazando un static ¡bloquee con código en un constructor como lo propuso! Además, esto no funcionará si necesita inicializar varios static campos de manera interrelacionada.

Un caso donde un static block sería incómodo de reemplazar sería una clase de “coordinador” que necesita inicializar varias otras clases exactamente una vez, especialmente incómodo si implica inyección de dependencia.

public class Coordinator {
    static {
        WorkerClass1.init();
        WorkerClass2.init(WorkerClass1.someInitializedValue);
        // etc.
    }
}

Particularmente si no quiere conectar ninguna dependencia en WorkerClass2 en WorkerClass1, se necesita algún tipo de código coordinador como este. Este tipo de cosas definitivamente no pertenecen a un constructor.

Tenga en cuenta que también hay algo llamado bloque inicializador de instancia. Es un bloque de código anónimo que se ejecuta cuando se crea cada instancia. (La sintaxis es como un static bloque, pero sin el static palabra clave.) Es particularmente útil para clases anónimas, porque no pueden tener constructores con nombre. He aquí un ejemplo del mundo real. Desde (insondable) GZIPOutputStream no tiene un constructor ni ninguna llamada api con la que pueda especificar un nivel de compresión, y el nivel de compresión predeterminado es ninguno, necesita subclasificar GZIPOutputStream para obtener cualquier compresión. Siempre puedes escribir una subclase explícita, pero puede ser más conveniente escribir una clase anónima:

OutputStream os = . . .;
OutputStream gzos = new GZIPOutputStream(os) {
    {
        // def is an inherited, protected field that does the actual compression
        def = new Deflator(9, true); // maximum compression, no ZLIB header
    }
};

  • ¿Por qué no puedo hacerlo dentro del constructor?

    – Bestia Sexy

    21 de diciembre de 2012 a las 18:08

  • Otro uso típico de los inicializadores estáticos es la carga de una biblioteca nativa para clases que tienen métodos nativos, como este: static { System.loadLibrary ("foo"); }

    – biziclop

    21 de diciembre de 2012 a las 18:11

  • @Cupidvogel: dado que la variable se declara tanto static y final (para que no se pueda cambiar), el lenguaje no te permitirá hacerlo en el constructor.

    –Ted Hopp

    21 de diciembre de 2012 a las 18:13

  • @Cupidvogel, ¿entiende el concepto de una clase y la creación de instancias? Si coloca ese código en el constructor, cada vez que cree una nueva instancia, la misma entrada se agregará nuevamente al mapa. Desea que este comportamiento ocurra una vez en el nivel de clase, no para todas las instancias.

    – John

    21 de diciembre de 2012 a las 18:15

  • @Cupidvogel: creo que lo que publiqué es un caso en el que un constructor no puede hacer algo que el bloque estático puede hacer. (Tenga en cuenta que este no es un caso en el que un constructor no debe ser usado; es un caso donde un constructor no poder ser usado.)

    –Ted Hopp

    21 de diciembre de 2012 a las 18:19


avatar de usuario
Subín Sebastián

El constructor se invoca al crear una instancia de la clase.

El bloque estático se invoca cuando un cargador de clases carga esta definición de clase, para que podamos inicializar miembros estáticos de esta clase. No deberíamos inicializar miembros estáticos del constructor, ya que son parte de la definición de clase, no un objeto.

  • Por favor explique lo que quiere decir con ‘cargar’.

    – Bestia Sexy

    21 de diciembre de 2012 a las 18:01

  • Cada clase debe cargarse en jvm a través de un cargador de clases. Puede suceder cuando se solicita un objeto del tipo de esa clase en el código o se solicita una variable estática en el código. Por ejemplo: int b= ClassA.staticMenber; staticMember es una variable estática presente en ClassA, que se puede inicializar en un bloque estático.

    – Subín Sebastián

    21 de diciembre de 2012 a las 18:05


  • Entonces, esencialmente quiere decir ‘cuando una instancia de esa clase se crea por primera vez’ por ‘cargar’, ¿verdad?

    – Bestia Sexy

    21 de diciembre de 2012 a las 18:07

  • por favor vea la edición. puede ser cuando se crea una instancia o se hace una referencia estática.

    – Subín Sebastián

    21 de diciembre de 2012 a las 18:08

  • +1 para No deberíamos inicializar miembros estáticos del constructor, ya que son parte de la definición de clase, no un objeto.

    – Un Gupta

    28/10/2014 a las 15:05


avatar de usuario
Akavall

El inicializador estático se ejecutará si inicializamos una clase, esto no requiere que instanciamos una clase. Pero el constructor se ejecuta solo cuando creamos una instancia de la clase.

Por ejemplo:

class MyClass
{   
    static
    {
        System.out.println("I am static initializer");
    }
    MyClass()
    {
        System.out.println("I am constructor");
    }

    static void staticMethod()
    {
        System.out.println("I am static method");
    }
}

Si ejecutamos:

MyClass.staticMethod();

Producción:

I am static initializer
I am static method

Nunca creamos una instancia, por lo que no se llama al constructor, pero se llama al inicializador estático.

Si creamos una instancia de una clase, se ejecutan tanto el iniciador estático como el constructor. No hay sorpresas.

MyClass x = new MyClass();

Producción:

I am static initializer
I am constructor

Tenga en cuenta que si ejecutamos:

MyClass x;

Salida: (vacío)

Declaración de variable x no requiere MyClass para ser inicializado, por lo que el inicializador estático no se ejecuta.

  • Estás malinterpretando lo que está pasando. Bloques estáticos son se ejecuta cuando se inicializa la clase. Es solo que simplemente declarando una variable MyClass x no requiere que la clase sea cargada o inicializada. Java es lo suficientemente inteligente como para saber que puede diferir la carga e inicialización de la clase hasta que realmente se necesite algo al respecto (aparte de su nombre). (Por cierto, simplemente hacer referencia a un campo estático de la clase también hará que la clase se inicialice. No necesita crear una instancia o llamar a un método).

    –Ted Hopp

    21 de diciembre de 2012 a las 19:42


  • ¡Gracias por la aclaración!

    – Akavall

    21 de diciembre de 2012 a las 19:45

  • @Ted Hopp Edité mi respuesta para reflejar su punto, que básicamente es la esencia de mi respuesta ahora.

    – Akavall

    21 de diciembre de 2012 a las 19:51


  • Mucho mejor. Sin embargo, no veo cómo esto aborda la pregunta de OP.

    –Ted Hopp

    21 de diciembre de 2012 a las 19:55

  • @Ted Hopp, el OP básicamente pregunta sobre la diferencia entre el inicializador estático y el constructor. La última pregunta de OP es “¿hay algo que el bloque estático logre que un constructor no pueda”. Mi respuesta ilustra que el inicializador estático se ejecuta cuando la instancia de de MyClass no se crea, mientras que el constructor no lo es. Esto es algo que el inicializador estático puede hacer y el constructor no puede hacer.

    – Akavall

    21 de diciembre de 2012 a las 20:04

avatar de usuario
marca byers

El inicializador estático se ejecuta cuando se carga la clase, incluso si nunca crea ningún objeto de ese tipo.

  • No todas las clases están destinadas a ser instanciadas. Es posible que nunca se llame al constructor. Incluso podría ser privado.
  • Es posible que desee acceder a los campos estáticos de la clase antes de ejecutar un constructor.
  • El inicializador estático solo se ejecuta una vez cuando se carga la clase. Se llama al constructor para cada objeto de ese tipo que instancias.

No puede inicializar variables estáticas con un constructor, o al menos probablemente no deberíay no será particularmente útil.

Especialmente cuando intenta inicializar constantes estáticas que requieren una lógica significativa para generar, eso realmente debería suceder en un bloque estático, no en un constructor.

  • No fui el votante negativo, pero sospecho que el problema fue que técnicamente puedes inicializar variables estáticas en un constructor.

    – biziclop

    21 de diciembre de 2012 a las 18:07

  • Estoy completamente de acuerdo contigo, solo era una teoría sobre el motivo del voto negativo.

    – biziclop

    21 de diciembre de 2012 a las 18:15

avatar de usuario
nathan hughes

Son dos cosas separadas. Utiliza un constructor para inicializar una instancia de una clase, el bloque de inicialización estática inicializa los miembros estáticos en el momento en que se carga la clase.

  • No fui el votante negativo, pero sospecho que el problema fue que técnicamente puedes inicializar variables estáticas en un constructor.

    – biziclop

    21 de diciembre de 2012 a las 18:07

  • Estoy completamente de acuerdo contigo, solo era una teoría sobre el motivo del voto negativo.

    – biziclop

    21 de diciembre de 2012 a las 18:15

avatar de usuario
eppesuig

El bloque estático es muy útil cuando tiene que realizar alguna acción, incluso si aún no se han creado instancias. Como ejemplo, para inicializar una variable estática con valor no estático.

¿Ha sido útil esta solución?