Appartenere alla classe, non all'oggetto
La maggior parte dei campi e dei metodi che hai scritto finora appartiene agli oggetti: ogni volta che chiami new, ogni istanza riceve la propria copia dei campi, e i metodi lavorano sullo stato di quell'oggetto specifico. La parola chiave static ribalta tutto questo. Un membro static appartiene alla classe stessa: ne esiste una sola copia, condivisa da tutte le istanze, ed esiste anche se non crei mai un oggetto.
Questa sola distinzione spiega i campi statici, i metodi statici, le costanti e perfino perché main è sempre static.
Un campo static condiviso
Un campo non statico dà a ogni oggetto il proprio spazio. Un campo static dà alla classe un solo spazio condiviso da tutti. L'esempio classico è un contatore che tiene traccia di quanti oggetti sono stati creati:
Nota che leggi il contatore come User.count, tramite il nome della classe, perché il valore non appartiene a nessun User in particolare. Ogni name, invece, vive dentro il proprio oggetto. Se modifichi count tramite un'istanza qualsiasi, tutte le istanze vedono il nuovo valore, perché ne esiste uno solo.
Metodi static
Anche un metodo static appartiene alla classe, quindi lo chiami sulla classe senza un oggetto:
È esattamente lo schema usato nella libreria standard: Math.max, Integer.parseInt, Arrays.sort e List.of sono tutti static, cioè comportamenti di utilità che non hanno bisogno di un oggetto su cui lavorare. Usa un metodo static quando il lavoro dipende solo dai suoi argomenti, non dallo stato di un oggetto.
La grande trappola: static non vede l'istanza
Un metodo static viene eseguito senza alcun oggetto, quindi non c'è nessun this. Significa che non può toccare campi di istanza o chiamare direttamente metodi di istanza: non c'è un oggetto specifico da cui leggerli. È l'errore più comune in assoluto per chi inizia con static:
class Account {
int balance = 100; // campo di istanza
static int show() {
return balance; // ERRORE DI COMPILAZIONE: il campo non statico 'balance'
} // non può essere usato da un contesto statico
}
La soluzione è trasformare il metodo in un metodo di istanza (togli static, così ha un this) oppure passargli l'oggetto in modo esplicito:
La direzione opposta va bene: un metodo di istanza può leggere liberamente i campi static e chiamare i metodi static, perché i dati condivisi a livello di classe esistono sempre.
Costanti con static final
Combina static con final e ottieni una costante: un unico valore condiviso che non può mai cambiare. Per convenzione, le costanti si scrivono in UPPER_SNAKE_CASE:
static final è il modo idiomatico per esprimere "un valore fisso che appartiene al tipo", come Integer.MAX_VALUE o Math.PI nella libreria standard. Renderlo static significa non sprecare una copia per ogni oggetto; renderlo final significa che nessuno può riassegnarlo.
Blocchi di inizializzazione static
Un semplice campo static si può inizializzare sulla stessa riga. Quando la preparazione richiede una vera logica, come costruire una tabella di ricerca con una HashMap o leggere una configurazione, usa un blocco static. Viene eseguito una volta, quando la classe viene caricata per la prima volta, prima che esista qualsiasi oggetto:
Il blocco viene eseguito una sola volta, indipendentemente da quante volte usi la classe, e questo lo rende il posto giusto per una preparazione unica valida per tutta la classe.
Quando usare static
Una regola pratica veloce:
- Usa un campo static solo per dati davvero condivisi da tutte le istanze: un contatore, una cache, una costante. Se due oggetti potrebbero ragionevolmente contenere valori diversi, deve essere un campo di istanza.
- Usa un metodo static quando il risultato dipende solo dagli argomenti, non dallo stato di un oggetto (funzioni di utilità pure).
- Parti dai membri di istanza. Abusare di
statictrasforma silenziosamente il programma in un mucchio di stato globale, difficile da testare e da capire.staticè l'eccezione, non il punto di partenza.
Prossima pagina: enum
Una costante static final va bene per un singolo valore fisso, ma quando hai un piccolo insieme fisso di valori collegati (direzioni, giorni della settimana, stati di un ordine) Java ha un tipo apposito, più sicuro ed espressivo di tante costanti sparse. È l'enum, ed è l'argomento della prossima pagina.
Domande frequenti
Cosa significa static in Java?
static significa che un campo o un metodo appartiene alla classe stessa, non a un singolo oggetto. Esiste esattamente una copia di un campo static, condivisa da tutte le istanze, e un metodo static si chiama sulla classe (Math.max(...)) senza bisogno di un oggetto. I membri non statici (di istanza), invece, hanno una copia nuova per ogni oggetto.
Qual è la differenza tra variabili statiche e di istanza in Java?
Una variabile di istanza ha un valore per ogni oggetto: due oggetti possono contenere valori diversi. Una variabile static ha un solo valore condiviso da tutti gli oggetti della classe, quindi se la modifichi tramite un oggetto (o tramite il nome della classe) la modifica è visibile a tutti gli altri. Usa i campi di istanza per lo stato di ogni oggetto e i campi static per dati davvero globali per la classe, come un contatore o una costante.
Perché il metodo main è static in Java?
La JVM deve chiamare main prima che esista qualsiasi oggetto della tua classe. Poiché un metodo static appartiene alla classe e non a un'istanza, il runtime può invocare Main.main(args) direttamente senza costruire prima un Main. Ecco perché la firma è sempre public static void main(String[] args).