1. Orden de inicialización de un objeto en Java: ¿quién va primero y quién va último?
Crear un objeto en Java no es simplemente «reservar memoria y escribir valores en ella». Es todo un ritual con un orden de acciones bien definido. Si imaginamos el proceso de inicialización como el lanzamiento de un cohete, cada etapa tiene su lugar estrictamente establecido.
Cuando escribes:
Person vasya = new Person("Vasya", 30);
Java realiza los siguientes pasos:
- Reserva memoria para el objeto y asigna a todos los campos sus valores predeterminados.
- Ejecuta las inicializaciones explícitas de los campos (si les diste valor directamente al declararlos).
- Ejecuta todos los bloques de inicialización no estáticos (normales), en el orden en que aparecen en la clase.
- Ejecuta el cuerpo del constructor que invocaste mediante new.
Veamos cada paso en detalle.
2. Inicialización de campos
Valores predeterminados
Cuando el objeto acaba de crearse (antes de que asignes algo explícitamente), todos sus campos reciben valores predeterminados:
| Tipo de campo | Valor predeterminado |
|---|---|
|
|
|
|
|
|
|
'\u0000' (carácter nulo) |
| Tipos de referencia (String, otros objetos) | |
Inicialización explícita
Si al declarar un campo le asignas un valor de inmediato, ese valor se establecerá después de los valores predeterminados, pero antes de la ejecución del constructor.
public class Person {
private String name = "Sin nombre";
private int age = 18;
}
Si creas un objeto mediante el constructor vacío, esos valores se mantendrán. Si usas un constructor con parámetros, es muy probable que se sobrescriban.
3. Bloques de inicialización: para qué sirven y cómo funcionan
En Java se pueden declarar los llamados bloques de inicialización no estáticos. Se ejecutan cada vez que se crea un objeto, justo después de la inicialización de los campos, pero antes del constructor.
Sintaxis:
public class Person {
{
System.out.println("¡Se ejecuta un bloque de inicialización no estático!");
}
}
Si en la clase hay varios bloques de este tipo, se ejecutan en el orden en que están escritos.
Ejemplo con varios bloques
public class Person {
private String name = "Sin nombre";
{
System.out.println("Bloque 1: name = " + name);
name = "Misterio";
}
{
System.out.println("Bloque 2: name = " + name);
}
public Person() {
System.out.println("Constructor: name = " + name);
}
}
Si creamos un objeto:
Person p = new Person();
La salida será:
Bloque 1: name = Sin nombre
Bloque 2: name = Misterio
Constructor: name = Misterio
¿Para qué sirven los bloques de inicialización?
En la práctica, los bloques de inicialización se usan rara vez. Normalmente, todo lo que puede hacerse en un bloque puede hacerse bien al declarar el campo, bien en el constructor. Pero a veces (por ejemplo, si tienes varios constructores y parte de la lógica de inicialización debe ser común a todos) los bloques de inicialización pueden resultar útiles.
4. Constructor: el acorde final de la inicialización
Después de que todos los campos hayan recibido sus valores (predeterminados o explícitos) y se hayan ejecutado todos los bloques de inicialización, se invoca el constructor, precisamente el que indicaste después de new.
En el constructor normalmente estableces los valores finales de los campos, recibes parámetros y realizas otra inicialización que depende de los datos de entrada.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
System.out.println("Constructor: name = " + name + ", age = " + age);
this.name = name;
this.age = age;
}
}
5. Demostración del orden completo de inicialización
Escribamos una clase que muestre claramente en qué orden se produce la inicialización.
public class Person {
private String name = "Sin nombre";
private int age = 18;
{
System.out.println("Bloque de inicialización: name = " + name + ", age = " + age);
age = 21;
}
public Person() {
System.out.println("Constructor sin parámetros: name = " + name + ", age = " + age);
}
public Person(String name, int age) {
System.out.println("Constructor con parámetros: name = " + name + ", age = " + age);
this.name = name;
this.age = age;
}
public void printInfo() {
System.out.println("Person: name = " + name + ", age = " + age);
}
}
Y ahora en nuestra clase principal (por ejemplo, Main):
public class Main {
public static void main(String[] args) {
System.out.println("Creando el objeto p1:");
Person p1 = new Person();
p1.printInfo();
System.out.println("\nCreando el objeto p2:");
Person p2 = new Person("Petya", 30);
p2.printInfo();
}
}
La salida será aproximadamente así:
Creando el objeto p1:
Bloque de inicialización: name = Sin nombre, age = 18
Constructor sin parámetros: name = Sin nombre, age = 21
Person: name = Sin nombre, age = 21
Creando el objeto p2:
Bloque de inicialización: name = Sin nombre, age = 18
Constructor con parámetros: name = Sin nombre, age = 21
Person: name = Petya, age = 30
Ten en cuenta: en el constructor con parámetros los valores de los campos aún son los anteriores (los que quedan tras el bloque de inicialización), y solo después del constructor los campos reciben los valores finales.
6. Diagrama del orden de inicialización
Aquí tienes un pequeño esquema (diagrama de bloques) para visualizarlo:
flowchart TD
A[Asignación de memoria, valores predeterminados] --> B[Inicialización explícita de campos]
B --> C[Ejecución de bloques de inicialización no estáticos]
C --> D[Ejecución del constructor]
D --> E[El objeto está listo para usarse]
7. Particularidades y matices
Campos estáticos y bloques estáticos
En esta lección hablamos solo de campos y bloques no estáticos (normales). Los campos y bloques estáticos se inicializan una sola vez al cargarse la clase, no en cada creación de objeto. Hablaremos de ello con más detalle en los temas de encapsulación.
¿Cuándo se inicializa qué?
- Campos — en cada creación de objeto.
- Bloques de inicialización — en cada creación de objeto.
- Campos y bloques estáticos — solo una vez al cargarse la clase.
¿Se puede acceder a los campos en los bloques de inicialización?
¡Sí, se puede! Pero es importante recordar que si el campo está declarado debajo del bloque, ya existe; y si accedes a él antes de su inicialización explícita, obtendrás el valor predeterminado.
8. Errores típicos al inicializar objetos
Error n.º 1: esperar que los campos se inicialicen antes de su declaración.
En Java, el orden de declaración de campos y bloques de inicialización en la clase importa. Si en un bloque de inicialización accedes a un campo declarado más abajo, este ya existe, pero aún no está inicializado explícitamente: tendrá el valor predeterminado.
Error n.º 2: duplicar la inicialización en bloques y constructores.
Los principiantes suelen repetir la misma lógica tanto en el bloque como en el constructor. Es mejor usar o bien el bloque, o bien el constructor, o invocar un constructor desde otro mediante this(...).
Error n.º 3: esperar que los campos estáticos se inicialicen en cada creación de objeto.
Los campos y bloques estáticos funcionan de otro modo: se inicializan una sola vez al cargarse la clase.
Error n.º 4: usar campos de referencia no inicializados.
Si olvidas inicializar explícitamente un campo de referencia (por ejemplo, String name;), entonces será null hasta que le asignes un valor explícito en un bloque de inicialización o en un constructor.
GO TO FULL VERSION