Parola chiave static in Java
Dichiara membri a livello di classe in Java con static — campi, metodi, blocchi e classi annidate statiche.
static è la parola chiave che promuove un membro di una classe da "uno per istanza" a "uno per classe." Un campo statico ha una sola copia condivisa da ogni oggetto. Un metodo statico appartiene alla classe stessa e viene eseguito senza alcuna istanza. Un blocco statico viene eseguito una volta sola, quando la classe viene caricata per la prima volta. Una classe annidata statica vive all'interno di un'altra classe senza necessitare di un oggetto esterno.
La regola fondamentale: i membri static non hanno un this. Tutto il resto del loro comportamento deriva da questo principio.
Campi statici
Un campo statico viene dichiarato con static nel corpo della classe. Esiste esattamente una copia, indipendentemente da quante istanze esistono:
public class Counter {
static int total; // one Counter.total for everybody
int count; // each Counter has its own count
}
Counter a = new Counter(); a.count++; Counter.total++;
Counter b = new Counter(); b.count++; Counter.total++;
System.out.println(a.count); // 1
System.out.println(b.count); // 1
System.out.println(Counter.total); // 2Si accede a un campo statico tramite il nome della classe (Counter.total) — questa è la forma canonica. Java consente anche di scrivere a.total poiché a è un Counter, ma ogni linter al mondo segnala questo stile come scorretto.
Metodi statici
Un metodo statico appartiene alla classe. Lo si invoca con ClassName.method(...):
public class MathUtil {
public static int square(int n) {
return n * n;
}
}
int x = MathUtil.square(7); // 49All'interno di un metodo statico non esiste this — non è possibile leggere campi di istanza o chiamare metodi di istanza senza qualificatore, perché non c'è alcuna istanza corrente da cui leggerli:
public class Counter {
int count;
static void reset() {
count = 0; // ERROR: cannot reference instance field from static context
}
}È comunque possibile ricevere un'istanza come parametro e operare su di essa:
static void resetThis(Counter c) {
c.count = 0; // ok — c is a parameter, not this
}La direzione inversa funziona senza problemi: un metodo di istanza può chiamare un metodo statico senza cerimonie, perché la classe è sempre disponibile anche quando esiste un'istanza.
Quando scegliere static
Il criterio unico è se il membro dipende dallo stato per-istanza.
Dipende da this? | Usare |
|---|---|
| No — calcolo puro, contatore condiviso, metodo factory | static |
| Sì — legge o scrive i campi dell'oggetto stesso | istanza |
public static int hoursToSeconds(int h) { return h * 3600; } // no this needed → static
public int rentalDays() { return days; } // reads this.days → instanceUn segnale comune di problemi: un metodo di istanza che ignora this. Sta mentendo sull'essere specifico per istanza — renderlo static elimina la necessità per il chiamante di creare un oggetto solo per usarlo.
Costanti statiche
La combinazione static final è la forma standard per le costanti:
public static final double TAX_RATE = 0.08;
public static final int MAX_RETRIES = 3;
public static final String DEFAULT_LOCALE = "en";Per convenzione, le costanti sono nominate in UPPER_SNAKE_CASE. Vengono incorporate in fase di compilazione quando il valore è primitivo o un letterale String, il che le rende veloci quanto un letterale nel sito di chiamata.
Blocchi statici
Un blocco static { ... } viene eseguito una volta, quando la classe viene caricata per la prima volta nella JVM. Usarlo per la configurazione iniziale dei campi statici che richiedono più di una singola espressione:
public class Lookup {
static final Map<String, Integer> WEEKDAYS = new HashMap<>();
static {
WEEKDAYS.put("Mon", 1);
WEEKDAYS.put("Tue", 2);
WEEKDAYS.put("Wed", 3);
WEEKDAYS.put("Thu", 4);
WEEKDAYS.put("Fri", 5);
WEEKDAYS.put("Sat", 6);
WEEKDAYS.put("Sun", 7);
}
}I blocchi statici sono potenti ma facili da abusare. Preferire un semplice inizializzatore di campo quando una sola espressione è sufficiente.
I campi statici e i blocchi statici vengono eseguiti dall'alto verso il basso nell'ordine in cui compaiono nel sorgente. Se un inizializzatore accede a un campo (tramite un metodo) prima che la riga di quel campo sia stata eseguita, vede il valore predefinito (0, false o null) — quindi l'ordine è importante:
public class Order {
static int a = compute(); // runs first; b is still 0 → a becomes 1
static int b = 10; // runs second
static int compute() { return b + 1; }
}
// Order.a == 1, Order.b == 10(Leggere b direttamente in quella prima riga — static int a = b + 1; — costituisce invece un errore di compilazione "illegal forward reference"; solo il percorso indiretto attraverso un metodo espone il valore predefinito.)
Classi annidate statiche
Una classe dichiarata static all'interno di un'altra classe è una classe annidata statica. A differenza di una classe interna non statica, non porta un riferimento implicito a un'istanza della classe esterna:
public class Outer {
static class Inner { // does not need an Outer instance
void hello() { System.out.println("hi"); }
}
}
Outer.Inner i = new Outer.Inner(); // create directly
i.hello();Questo è il modo in cui le classi helper sono state racchiuse nello stesso file di main nei blocchi static class Foo {...} precedenti — main è statico, quindi occorre che la classe helper sia statica per poterla istanziare senza un'istanza esterna di Outer. Il capitolo sulle classi annidate tratta tutte e quattro le varianti.
static non è "il costruttore"
I principianti talvolta confondono "static = viene eseguito una volta" con "static = viene eseguito alla creazione degli oggetti." L'inizializzazione statica avviene una volta per classe, non una volta per oggetto. Il costruttore viene eseguito una volta per oggetto. Sono meccanismi distinti e si attivano in situazioni diverse.
Membri statici nelle interfacce
Anche le interfacce possono avere metodi static. Si comportano come gli statici di classe — si invocano con InterfaceName.method(...):
public interface Path {
static Path of(String s) { return new SimplePath(s); }
}
Path p = Path.of("/tmp/foo");Il capitolo sulle interfacce tratta quando questo è una buona idea (metodi factory sull'interfaccia stessa).
Un esempio completo
Questo programma stampa:
#1 apple base=0.50 USD, with tax=0.54
#2 bread base=2.40 USD, with tax=2.59
#3 butter base=3.10 USD, with tax=3.35
Total items ever created: 3Si noti come l'esempio combina tutte e quattro le varianti: le costanti e la tabella di ricerca vengono popolate prima che esista qualsiasi Item, ogni Item mantiene il proprio name e basePrice, e il singolo campo condiviso itemsCreated assegna l'id progressivo.
Cosa viene dopo
static permette di dire chi possiede un membro. Il prossimo complemento è final, che permette di dire se può cambiare. Insieme, static final è la forma standard per le costanti; da solo, final influenza l'ereditarietà e il design immutabile. Continua con java-final.