1. Qué son los bloques de inicialización
En Java hay dos tipos de bloques de inicialización:
- Bloques de inicialización no estáticos (normales) — se ejecutan en cada creación de un nuevo objeto, justo después de la inicialización de los campos y antes de llamar al constructor.
- Bloques de inicialización estáticos — se ejecutan una sola vez al cargar la clase en memoria (antes de crear el primer objeto o de acceder a campos/métodos estáticos).
Bloque de inicialización no estático
Se declara directamente en el cuerpo de la clase, sin ninguna palabra como static:
public class User {
private String name;
// Bloque de inicialización no estático
{
System.out.println("¡Se ejecuta el bloque no estático!");
name = "Nombre por defecto";
}
public User() {
System.out.println("¡Se ejecuta el constructor!");
}
}
Bloque de inicialización estático
Se declara con la palabra clave static:
public class Config {
public static String appName;
static {
System.out.println("¡Se ejecuta el bloque estático!");
appName = "Mi superaplicación";
}
}
2. Orden de inicialización: ¿quién manda?
En Java el orden de inicialización de los elementos de una clase no es «al tuntún», sino una secuencia estrictamente definida. Si alguna vez has intentado montar un mueble de IKEA sin instrucciones, entenderás por qué este orden es importante: si confundes los pasos, en lugar de un armario tendrás una obra de arte.
Orden de inicialización:
- Campos estáticos y bloques estáticos — en el orden en que se declaran en la clase. Se ejecutan una sola vez al cargar la clase.
- Campos no estáticos y bloques no estáticos — en el orden en que se declaran, en cada creación de un nuevo objeto.
- Constructor — se ejecuta después de todas las inicializaciones no estáticas.
Esquemáticamente
+-------------------------------+
| Carga de la clase en la JVM |
+-------------------------------+
| 1. Campos estáticos |
| 2. Bloques estáticos |
| ↓ |
| Creación del objeto |
| ↓ |
| 3. Campos no estáticos |
| 4. Bloques no estáticos |
| 5. Constructor |
+-------------------------------+
Ejemplo con salida
Escribamos una clase que nos muestre en qué orden ocurre todo:
public class Demo {
static String staticField = print("1. campo estático");
static {
print("2. bloque estático");
}
String field = print("3. campo no estático");
{
print("4. bloque no estático");
}
public Demo() {
print("5. constructor");
}
static String print(String msg) {
System.out.println(msg);
return msg;
}
public static void main(String[] args) {
System.out.println("Creamos el primer objeto Demo:");
Demo d1 = new Demo();
System.out.println("\nCreamos el segundo objeto Demo:");
Demo d2 = new Demo();
}
}
¿Qué saldrá en pantalla?
1. campo estático
2. bloque estático
Creamos el primer objeto Demo:
3. campo no estático
4. bloque no estático
5. constructor
Creamos el segundo objeto Demo:
3. campo no estático
4. bloque no estático
5. constructor
Ten en cuenta: las partes estáticas (static) se ejecutan solo una vez — en el primer acceso a la clase. Todo lo que no es static se ejecuta cada vez que se crea un objeto.
3. Ejemplos de código: para qué sirven los bloques de inicialización
Cuando el constructor no basta
A veces ocurre que parte de la inicialización debe ser común a todos los constructores. Por ejemplo, si una clase tiene varios constructores y no quieres duplicar la misma inicialización en cada uno de ellos. En ese caso, conviene extraerla a un bloque no estático:
public class Person {
private String id;
private String name;
{
// Este código se ejecutará antes de cualquier constructor
id = java.util.UUID.randomUUID().toString();
System.out.println("Generamos un id único: " + id);
}
public Person() {
System.out.println("Person() sin parámetros");
}
public Person(String name) {
this.name = name;
System.out.println("Person(String name)");
}
}
Resultado: al crear cualquier objeto Person primero se generará el id y después se ejecutará el constructor correspondiente.
Inicialización de datos estáticos complejos
El bloque estático se usa a menudo para inicializar datos estáticos «pesados» o complejos, por ejemplo, leer la configuración de un archivo, crear colecciones, conectarse a una base de datos, etc.
public class Settings {
public static final java.util.Map<String, String> DEFAULTS;
static {
DEFAULTS = new java.util.HashMap<>();
DEFAULTS.put("theme", "light");
DEFAULTS.put("language", "ru");
System.out.println("Bloque estático de Settings: ajustes por defecto");
}
}
4. Detalles útiles
Cuándo usar bloques de inicialización
Cuándo conviene usarlos
- Para una inicialización común necesaria en todos los constructores.
- Para datos estáticos complejos que no se pueden expresar con una simple asignación.
- Para inicializar recursos estáticos (por ejemplo, leer un archivo de configuración al iniciar la aplicación).
Cuándo NO conviene usarlos
- Si basta con una asignación normal o un constructor — úsalos.
- No «ocultes» la lógica de negocio en bloques de inicialización — dificulta la lectura y el mantenimiento del código.
- Si la inicialización depende de parámetros del constructor, usa el propio constructor.
No abuses de los bloques de inicialización
Los bloques de inicialización son una herramienta potente, aunque no la más usada. En la mayoría de los casos basta con una simple asignación o con el constructor. Si una clase tiene demasiados bloques de inicialización, el código se vuelve ilegible y difícil de mantener.
No uses un bloque no estático para lógica que dependa de los parámetros del constructor
En un bloque no estático no se pueden usar parámetros del constructor, ya que se ejecuta ANTES que el constructor. Si necesitas inicializar algo en función de los parámetros, hazlo en el propio constructor.
Bloques estáticos y herencia
Los bloques de inicialización estáticos no se heredan. Cada clase tiene su propio bloque estático. Al cargar una clase hija, primero se ejecuta el bloque estático de la base y después el bloque estático de la hija.
5. Errores típicos al trabajar con bloques de inicialización
Error n.º 1: Esperar que un bloque no estático vea los parámetros del constructor.
Muchos principiantes intentan usar parámetros del constructor en un bloque no estático, pero obtienen un error de compilación o un resultado inesperado. Recuerda: el bloque no estático se ejecuta antes del constructor, por lo que aún no hay parámetros.
Error n.º 2: Demasiada lógica en los bloques de inicialización.
Si en los bloques de inicialización aparece lógica compleja, el código se vuelve confuso. Es mejor hacer el trabajo principal en el constructor o en métodos separados.
Error n.º 3: Varios bloques estáticos con distinto orden.
Si una clase tiene varios bloques estáticos, se ejecutan en el orden en que están declarados en el código, junto con los campos estáticos. A veces esto lleva a resultados inesperados si, por ejemplo, un bloque depende del resultado de otro.
Error n.º 4: Esperar que el bloque estático se ejecute en cada creación de objeto.
El bloque static se ejecuta solo una vez — al cargar la clase. Si cuentas con una reinicialización repetida, no ocurrirá.
Error n.º 5: Intentar acceder a campos no estáticos desde un bloque estático.
En un bloque estático solo están disponibles las variables y los métodos estáticos. Intentar acceder a campos no estáticos (normales) provocará un error de compilación.
GO TO FULL VERSION