1. Che cosa sono i blocchi di inizializzazione
In Java ci sono due tipi di blocchi di inizializzazione:
- Blocchi di inizializzazione non statici (di istanza) — vengono eseguiti a ogni creazione di un nuovo oggetto, subito dopo l’inizializzazione dei campi e prima della chiamata del costruttore.
- Blocchi di inizializzazione statici — vengono eseguiti una sola volta al caricamento della classe in memoria (prima della creazione del primo oggetto o dell’accesso a campi/metodi statici).
Blocco di inizializzazione non statico
Si dichiara direttamente nel corpo della classe, senza alcuna parola chiave come static:
public class User {
private String name;
// Blocco di inizializzazione non statico
{
System.out.println("Si esegue il blocco non statico!");
name = "Nome predefinito";
}
public User() {
System.out.println("Si esegue il costruttore!");
}
}
Blocco di inizializzazione statico
Si dichiara con la parola chiave static:
public class Config {
public static String appName;
static {
System.out.println("Si esegue il blocco statico!");
appName = "La mia super applicazione";
}
}
2. Ordine di inizializzazione: chi viene prima?
In Java l’ordine di inizializzazione degli elementi della classe non è «a caso», ma una sequenza rigorosamente definita. Se hai mai provato a montare un mobile IKEA senza istruzioni, capirai perché questo ordine è importante: se sbagli i passaggi, invece di un armadio otterrai un oggetto artistico.
Ordine di inizializzazione:
- Campi statici e blocchi statici — nell’ordine in cui sono dichiarati nella classe. Vengono eseguiti una volta al caricamento della classe.
- Campi non statici e blocchi non statici — nell’ordine in cui sono dichiarati, a ogni creazione di un nuovo oggetto.
- Costruttore — viene eseguito dopo tutte le inizializzazioni non statiche.
Schema
+-------------------------------+
| Caricamento della classe |
| nella JVM |
+-------------------------------+
| 1. Campi statici |
| 2. Blocchi statici |
| ↓ |
| Creazione dell'oggetto |
| ↓ |
| 3. Campi non statici |
| 4. Blocchi non statici |
| 5. Costruttore |
+-------------------------------+
Esempio con output
Scriviamo una classe che ci mostri in quale ordine avviene tutto:
public class Demo {
static String staticField = print("1. campo static");
static {
print("2. blocco static");
}
String field = print("3. campo non statico");
{
print("4. blocco non statico");
}
public Demo() {
print("5. costruttore");
}
static String print(String msg) {
System.out.println(msg);
return msg;
}
public static void main(String[] args) {
System.out.println("Creiamo il primo oggetto Demo:");
Demo d1 = new Demo();
System.out.println("\nCreiamo il secondo oggetto Demo:");
Demo d2 = new Demo();
}
}
Cosa apparirà sullo schermo?
1. campo static
2. blocco static
Creiamo il primo oggetto Demo:
3. campo non statico
4. blocco non statico
5. costruttore
Creiamo il secondo oggetto Demo:
3. campo non statico
4. blocco non statico
5. costruttore
Nota bene: le parti statiche (static) vengono eseguite solo una volta — al primo accesso alla classe. Tutto ciò che non è static viene eseguito ogni volta che si crea un oggetto.
3. Esempi di codice: a cosa servono i blocchi di inizializzazione
Quando il costruttore non basta
A volte una parte dell’inizializzazione deve essere comune a tutti i costruttori. Per esempio, la classe ha più costruttori e non vuoi ripetere la stessa inizializzazione in ciascuno di essi. In questo caso è comodo spostarla in un blocco non statico:
public class Person {
private String id;
private String name;
{
// Questo codice verrà eseguito prima di qualsiasi costruttore
id = java.util.UUID.randomUUID().toString();
System.out.println("Generiamo un ID univoco: " + id);
}
public Person() {
System.out.println("Person() senza parametri");
}
public Person(String name) {
this.name = name;
System.out.println("Person(String name)");
}
}
Risultato: alla creazione di qualsiasi oggetto Person l’ID verrà generato per primo e poi verrà eseguito il costruttore appropriato.
Inizializzazione di dati statici complessi
Il blocco statico si usa spesso per inizializzare campi statici «pesanti» o complessi, ad esempio leggere una configurazione da file, creare collezioni, connettersi a un database ecc.
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("Blocco statico di Settings: impostazioni predefinite");
}
}
4. Dettagli utili
Quando usare i blocchi di inizializzazione
Quando conviene usarli
- Per un’inizializzazione comune necessaria in tutti i costruttori.
- Per dati statici complessi che non si possono esprimere con una semplice assegnazione.
- Per inizializzare risorse statiche (ad esempio, leggere un file di configurazione all’avvio dell’applicazione).
Quando NON conviene usarli
- Se bastano una semplice assegnazione o il costruttore — usa quelli.
- Non «nascondere» la logica di business nei blocchi di inizializzazione — rende il codice più difficile da leggere e mantenere.
- Se l’inizializzazione dipende dai parametri del costruttore, usala nel costruttore stesso.
Non abusare dei blocchi di inizializzazione
I blocchi di inizializzazione sono uno strumento potente, ma non tra i più usati. Nella maggior parte dei casi basta una semplice assegnazione o un costruttore. Se in una classe ci sono troppi blocchi di inizializzazione, il codice diventa poco leggibile e difficile da mantenere.
Non usare un blocco non statico per logica che dipende dai parametri del costruttore
In un blocco non statico non si possono usare i parametri del costruttore, perché viene eseguito PRIMA del costruttore. Se devi inizializzare qualcosa in base ai parametri, fallo nel costruttore.
Blocchi statici ed ereditarietà
I blocchi di inizializzazione statici non si ereditano. Ogni classe ha il proprio blocco statico. Al caricamento di una classe figlia, si esegue prima il blocco statico della base, poi quello della figlia.
5. Errori tipici nell’uso dei blocchi di inizializzazione
Errore n. 1: aspettarsi che un blocco non statico veda i parametri del costruttore.
Molti principianti cercano di usare i parametri del costruttore in un blocco non statico, ma ottengono un errore di compilazione o un risultato inatteso. Ricorda: il blocco non statico viene eseguito PRIMA del costruttore, quindi non ci sono ancora parametri.
Errore n. 2: troppa logica nei blocchi di inizializzazione.
Se nei blocchi di inizializzazione compare logica complessa, il codice diventa contorto. Meglio svolgere il lavoro principale nel costruttore o in metodi separati.
Errore n. 3: più blocchi statici con ordine diverso.
Se in una classe ci sono più blocchi statici, vengono eseguiti nell’ordine in cui sono dichiarati nel codice, insieme ai campi statici. A volte ciò porta a risultati inattesi, se ad esempio un blocco dipende dal risultato di un altro.
Errore n. 4: aspettarsi che il blocco statico venga eseguito a ogni creazione di oggetto.
static viene eseguito una sola volta — al caricamento della classe. Se conti su una reinizializzazione, non avverrà.
Errore n. 5: tentare di accedere a campi non statici da un blocco statico.
In un blocco statico sono accessibili solo variabili e metodi statici. Tentare di accedere a campi non statici (d’istanza) provocherà un errore di compilazione.
GO TO FULL VERSION