Ecco degli esercizi con soluzione per praticare l’uso del pattern Singleton in Java.
Esercizio 1: Implementazione di Base del Singleton
Creare una classe Singleton semplice che non supporta il multithreading.
public class Singleton { private static Singleton istanza;
private Singleton() { // Costruttore privato per impedire l'instanziazione }
public static Singleton getInstance() { if (istanza == null) { istanza = new Singleton(); } return istanza; }
public void mostraMessaggio() { System.out.println("Singleton: istanza unica"); }
public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); singleton.mostraMessaggio(); }}
Esercizio 2: Singleton con Multithreading
Modificare la classe Singleton per renderla thread-safe utilizzando il blocco sincronizzato.
public class SingletonThreadSafe { private static SingletonThreadSafe istanza;
private SingletonThreadSafe() { // Costruttore privato per impedire l'instanziazione }
public static synchronized SingletonThreadSafe getInstance() { if (istanza == null) { istanza = new SingletonThreadSafe(); } return istanza; }
public void mostraMessaggio() { System.out.println("Singleton thread-safe: istanza unica"); }
public static void main(String[] args) { SingletonThreadSafe singleton = SingletonThreadSafe.getInstance(); singleton.mostraMessaggio(); }}
Esercizio 3: Singleton con Doppio Controllo di Blocco
Implementare un Singleton utilizzando il doppio controllo di blocco per migliorare le prestazioni.
public class SingletonDoubleChecked { private static volatile SingletonDoubleChecked istanza;
private SingletonDoubleChecked() { // Costruttore privato per impedire l'instanziazione }
public static SingletonDoubleChecked getInstance() { if (istanza == null) { synchronized (SingletonDoubleChecked.class) { if (istanza == null) { istanza = new SingletonDoubleChecked(); } } } return istanza; }
public void mostraMessaggio() { System.out.println("Singleton con doppio controllo di blocco: istanza unica"); }
public static void main(String[] args) { SingletonDoubleChecked singleton = SingletonDoubleChecked.getInstance(); singleton.mostraMessaggio(); }}
Esercizio 4: Singleton con Caricamento Pigro
Implementare un Singleton utilizzando il caricamento pigro con una classe interna statica.
public class SingletonLazy { private SingletonLazy() { // Costruttore privato per impedire l'instanziazione }
private static class Holder { private static final SingletonLazy ISTANZA = new SingletonLazy(); }
public static SingletonLazy getInstance() { return Holder.ISTANZA; }
public void mostraMessaggio() { System.out.println("Singleton con caricamento pigro: istanza unica"); }
public static void main(String[] args) { SingletonLazy singleton = SingletonLazy.getInstance(); singleton.mostraMessaggio(); }}
Esercizio 5: Singleton con Enumerazione
Implementare un Singleton utilizzando un'enumerazione per garantire la thread-safety e la protezione dalla serializzazione.
public enum SingletonEnum { ISTANZA;
public void mostraMessaggio() { System.out.println("Singleton con enumerazione: istanza unica"); }
public static void main(String[] args) { SingletonEnum singleton = SingletonEnum.ISTANZA; singleton.mostraMessaggio(); }}
Esercizio 6: Singleton con Reflection
Proteggere un Singleton dalla creazione di nuove istanze tramite reflection.
public class SingletonProtected { private static SingletonProtected istanza; private static boolean istanzaCreata = false;
private SingletonProtected() { if (istanzaCreata) { throw new RuntimeException("Usa getInstance() per creare l'istanza"); } istanzaCreata = true; }
public static synchronized SingletonProtected getInstance() { if (istanza == null) { istanza = new SingletonProtected(); } return istanza; }
public void mostraMessaggio() { System.out.println("Singleton protetto dalla reflection: istanza unica"); }
public static void main(String[] args) { SingletonProtected singleton = SingletonProtected.getInstance(); singleton.mostraMessaggio(); }}