Bloque de inicialización

Un bloque de inicialización es un concepto en la programación orientada a objetos , principalmente conocido del lenguaje Java , que es una secuencia de comandos ejecutados al crear ( cargar ) clases y objetos . Diseñado para aumentar considerablemente el poder del constructor . Hay dos tipos: un bloque de inicialización estático, comúnmente llamado bloque estático para abreviar, y un bloque de inicialización dinámico (bloque de instancia).

Motivación

Cuando se crea un objeto, se ejecutan varios comandos especificados en el constructor. A veces se hace necesario ampliar las posibilidades de la sintaxis. Por lo general, un bloque dinámico existe solo por conveniencia: se puede reemplazar fácilmente agregando una función de carga y llamándola desde cada constructor. Sin embargo, el bloque estático aumenta considerablemente la funcionalidad del programa y, por lo tanto, se usa con mucha más frecuencia.

Bloque de inicialización estática

Un bloque estático es esencialmente un constructor para toda la clase. Su sintaxis es:

... static { // Código de bloque estático } ...

Se coloca entre definiciones de campo y funciones de clase. Los comandos se ejecutarán en uno de dos casos, lo que ocurra primero:

  1. Al crear el primer objeto de la clase en el transcurso del programa, antes de ejecutar el constructor.
  2. La primera vez que se llama a una función estática, antes de la ejecución.

Es decir, el código se ejecuta la primera vez que se carga la clase. En este ejemplo, queremos crear una clase que modele automóviles fabricados por una empresa en particular y los mantenga para realizar un seguimiento del resto de su existencia, incluida su situación actual, propietarios, historial de reparaciones, etc. Cada objeto es un automóvil , y en la clase hay un campo estático que contiene una base de datos de todos los autos. Basado en la estructura del mapa cuando la clave es un modelo de automóvil y el contenido es un grupo de automóviles de ese modelo. El siguiente código demuestra el uso de un bloque de inicialización estático:

Coche de clase pública { Mapa estático < Cadena , Conjunto < Coche >> catálogo ; estático { catalog = new HashMap < String , Set < Car >> (); catálogo _ poner ( "modelo105" , nuevo HashSet < Coche > ()); catálogo _ poner ( "modelo125" , nuevo HashSet < Coche > ()); catálogo _ poner ( "modelo140" , nuevo HashSet < Coche > ()); catálogo _ put ( "modelo201" , nuevo HashSet < Coche > ()); } coche público ( modelo de cadena ) { catálogo _ obtener ( modelo ). añadir ( esto ); // ... } // ... }

La línea 4 se puede conectar fácilmente a la línea 2 sin necesidad de un bloque estático. Sin embargo, las líneas 5-8 muestran la necesidad de ello: la capacidad de ejecutar comandos complejos a nivel de clase, que a nivel de objeto aparecerían en el constructor.

Bloque de inicialización dinámica

Un bloque dinámico es una adición a un constructor. Su sintaxis es:

... { // Código de bloque de instancia } ...

Se coloca entre definiciones de campo y funciones de clase. Los comandos se ejecutarán cuando se cree el objeto. El bloque dinámico es un complemento para hacer que el constructor sea más fácil de escribir y no trae funcionalidad adicional. Le permite guardar la creación de una función de lanzamiento y agregar su llamada desde todos los constructores. Por ejemplo, fragmento de código:

Coche de clase pública { recuento int estático = 0 ; coche público ( modelo de cadena ) { inicializar (); // ... } Coche público ( modelo String , precio doble ) { inicializar (); // ... } inicial vacío privado () { cuenta ++ ; sistema _ fuera _ println ( "Hola a todos, tenemos " + count + " carros ahora!" ); } // ... }

es equivalente al código:

Coche de clase pública { recuento int estático = 0 ; coche público ( modelo de cadena ) { // ... } Coche público ( modelo String , precio doble ) { // ... } { cuenta ++ ; sistema _ fuera _ println ( "Hola a todos, tenemos " + count + " carros ahora!" ); } // ... }

Orden de carga

Durante el diseño del lenguaje Java se estableció un orden de carga consistente. Durante la carga de clases, el orden es el siguiente:

  1. Definiciones de campos estáticos de clases padre.
  2. Inicialización de campos estáticos y ejecución de bloques estáticos de clases padre.
  3. Definiciones de campos estáticos de una clase.
  4. Inicializar campos estáticos y ejecutar bloques de clase estáticos.

Luego, cuando se crea el objeto, el orden es el siguiente:

  1. Definiciones de campos de objetos de las clases principales.
  2. Inicialización de campo y ejecución de bloques dinámicos de clases principales.
  3. Ejecución de constructores de clases padres.
  4. Definiciones de campos de objeto de su clase.
  5. Inicialización de campo y ejecución de bloques dinámicos de su clase.
  6. Ejecutando un constructor de su clase.

Cuando existe una cadena de ancestros, todas las acciones se realizan primero en el ancestro más lejano (objeto de clase) y luego en la cadena en el mismo orden hasta la clase actual.

Si hay más de un tipo en la misma sección anterior, los pasos se realizan en el orden en que aparecen en el programa. Por ejemplo, el siguiente código:

clase pública T { int estático i = 5 ; estático { yo = 10 ; } estático { yo = yo * 3 ; } }

asigna el valor 30 a la variable i en cada objeto, pero el código:

clase pública T { estático { yo = 10 ; } int estático i = 5 ; estático { yo = yo * 3 ; } }

asigna un valor de 15. Es decir, primero se crea un campo y luego se realizan todas las acciones en el orden especificado en el programa: el primer bloque, luego la inicialización del campo y luego el segundo bloque.

Posibles problemas

Usar una variable antes de que se defina

Al contrario de lo que cabría esperar, el siguiente código:

clase pública T { estático { yo = 5 ; yo = yo + 1 ; } int estático i = 5 ; }

falla al compilar en la línea 4 porque la variable derecha i se usó antes de definirla, aunque la línea 3 compila y se ejecuta sin problemas, aunque la i izquierda en la línea 4 no arroja un error, y a pesar de que durante la operación, cuando se llega al comienzo de la línea 4, la variable se definió y recibió un valor. Esto se debe a que la ubicación de las variables (por ejemplo, en la línea 3) se compara con la lista de variables definidas actualmente durante la ejecución del programa, incluidos todos los campos estáticos, y el uso de dicha variable se compara con la ubicación de la definición.

Variable estática local

Al contrario de lo que cabría esperar, el siguiente código:

clase pública T { estático { int i = 10 ; } public static void main ( String [] args ) { sistema _ fuera _ println ( i ); } }

fallará la compilación en la línea 6 debido a que la variable no está definida, porque definir una variable en un bloque estático no crea una variable estática, solo una variable local en ese bloque. Es decir, el código no es equivalente al código . static {int i = 10;}static int i = 10;

Véase también

Enlaces