JavaRush /Java Blog /Random-IT /Pausa caffè #143. Classi sigillate in Java 17. 4 modi per...

Pausa caffè #143. Classi sigillate in Java 17. 4 modi per implementare Singleton

Pubblicato nel gruppo Random-IT

Classi sigillate in Java 17

Fonte: Codippa In questo post vedremo le classi sigillate, una nuova funzionalità introdotta in Java 17, e come dichiararle e utilizzarle con esempi. Pausa caffè #143.  Classi sigillate in Java 17. 4 modi per implementare Singleton - 1Le classi sigillate sono apparse per la prima volta in Java 15 come funzionalità di anteprima e successivamente in Java 16 con lo stesso stato. Questa funzionalità è diventata pienamente operativa con il rilascio di Java 17 ( JEP 409 ).

Cosa sono le classi sigillate?

Una classe sigillata consente di limitare o selezionare sottoclassi. Una classe non può estendere una classe privata a meno che non sia nell'elenco delle classi figlie consentite della classe genitore. La classe viene sigillata utilizzando la parola chiave sealed . La classe sigillata deve essere seguita dalla parola chiave consent , insieme a un elenco di classi che possono estenderla. Ecco un esempio:
public sealed class Device permits Computer, Mobile {
}
Questa dichiarazione significa che Device può essere esteso solo dalle classi Computer e Mobile . Se qualsiasi altra classe tenta di estenderlo, verrà generato un errore del compilatore. Una classe che estende una classe sigillata deve avere la parola chiave final , seal o non seal nella sua dichiarazione . Quindi abbiamo una gerarchia di classi fissa. Come si collega tutto questo alla creazione di una classe secondaria?
  1. finale significa che non può essere ulteriormente sottoclassato.

  2. sigillato significa che dobbiamo dichiarare le classi per bambini con permessi .

  3. non sigillato significa che qui finisce la gerarchia genitore-figlio .

Ad esempio, Computer consente le classi Laptop e Desktop purché Laptop stesso rimanga non sigillato . Ciò significa che il laptop può essere esteso a classi come Apple , Dell , HP e così via.

Gli obiettivi principali dell’introduzione delle classi sigillate sono:

  1. Fino ad ora potevi limitare l'estensione di una classe solo utilizzando la parola chiave final . Una classe sigillata controlla quali classi possono estenderla includendole nell'elenco consentito.

  2. Permette inoltre alla classe di controllare quali di esse saranno le sue classi figlie.

Regole

Alcune regole da ricordare quando si utilizzano le classi sigillate:
  1. Una classe sigillata deve definire classi che possano estenderla utilizzando i permessi . Ciò non è richiesto se le classi figlie sono definite all'interno della classe genitore come classe interna.

  2. La classe figlia deve essere final , sigillata o non sigillata .

  3. Una classe figlia consentita deve estendere la sua classe sigillata genitore.

    Cioè, se la classe A sigillata consente la classe B, allora B deve estendere A.

  4. Se la classe sigillata è in un modulo, anche le classi figlie devono trovarsi nello stesso modulo o nello stesso pacchetto se la classe sigillata genitore si trova in un modulo senza nome.

  5. Solo le classi consentite direttamente possono estendere una classe sigillata. Cioè, se A è una classe sigillata che consente a B di estenderla, allora anche B è una classe sigillata che consente a C.

    Allora C può solo estendere B, ma non può estendere direttamente A.

Interfacce sigillate

Come le classi sigillate, anche le interfacce possono essere sigillate. Tale interfaccia può consentire la selezione delle sue interfacce o classi figlie, che possono estenderla utilizzando permessi . Ecco un buon esempio:
public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
In questo caso, l' interfaccia Device consente alle interfacce Electronic e Physical di estenderla e alla classe DeviceImpl per la successiva implementazione.

Documenti sigillati

Le classi sigillate possono essere utilizzate con le voci introdotte in Java 16. Una voce non può estendere una classe normale, quindi può solo implementare un'interfaccia privata. Inoltre, la notazione implica finale . Pertanto, una voce non può utilizzare la parola chiave consent perché non può essere una sottoclasse. Cioè, esiste solo una gerarchia a livello singolo con i record. Ecco un esempio:
public sealed interface Device permits Laptop {
}
public record Laptop(String brand) implement Device {
}

Supporto alla riflessione

Java Reflection fornisce il supporto per le classi sigillate. I due metodi seguenti sono stati aggiunti a java.lang.Class :

1. getPermitSubclasses()

Ciò restituisce un array java.lang.Class contenente tutte le classi consentite da questo oggetto classe. Esempio:
Device c = new Device();
Class<? extends Device> cz = c.getClass();
Class<?>[] permittedSubclasses = cz.getPermittedSubclasses();
for (Class<?> sc : permittedSubclasses){
  System.out.println(sc.getName());
}
Conclusione:
Computer portatile

2.isSealed()

Restituisce true se la classe o l'interfaccia in cui viene chiamata è sigillata. Per ora è tutto sulle classi sigillate aggiunte in Java 17. Spero che questo articolo sia stato informativo.

4 modi per implementare Singleton

Fonte: Medium Oggi imparerai diversi modi per implementare il modello di progettazione Singleton. Il modello di progettazione Singleton è ampiamente utilizzato nei progetti Java. Fornisce il controllo dell'accesso alle risorse, come una connessione socket o database. Una volta mi è stato chiesto di implementare un singleton durante un colloquio per una posizione di sviluppatore web presso una grande azienda di chip. Questa era la prima volta che facevo un colloquio per una posizione sul web e non ho fatto molta preparazione, quindi ho scelto la soluzione più difficile: l'istanziazione pigra. Il mio codice era corretto solo al 90% e non abbastanza efficiente, ho finito per perdere nel round finale... Quindi spero che il mio articolo ti sia utile.

Istanziazione anticipata

class Singleton {
    private Singleton() {}
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}
Poiché l'oggetto è già creato al momento dell'inizializzazione, non vi è alcun problema di sicurezza del thread qui, ma spreca risorse di memoria se nessuno lo utilizza.

Implementazione pigra

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
Quando si utilizza un modello di inizializzazione lazy, l'oggetto viene creato su richiesta. Tuttavia, questo metodo presenta un problema di sicurezza del thread: se due thread vengono avviati contemporaneamente sulla riga 5, creeranno due istanze Singleton. Per evitare ciò, dobbiamo aggiungere un lucchetto:
class Singleton {
    private Singleton() {}
    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
Double-Checked Locking (DCL): non è presente alcun blocco sulla riga 6, quindi questa riga funzionerà molto rapidamente se l'oggetto è già stato creato. Perché dobbiamo ricontrollare istanza == null ? Perché forse ci sono due thread introdotti alla riga 7: il primo ha avviato l'oggetto, il secondo attende il lock Singleton.class . Se non viene effettuato alcun controllo, il secondo thread ricreerà nuovamente l'oggetto singleton. Tuttavia, questo metodo è ancora pericoloso per i thread. La riga 9 può essere divisa in tre righe di codice byte:
  1. Allocare memoria.
  2. Oggetto di inizializzazione.
  3. Assegna l'oggetto al riferimento all'istanza.
Poiché la JVM può funzionare fuori servizio, la macchina virtuale potrebbe assegnare un oggetto a un riferimento all'istanza prima dell'inizializzazione. Un altro thread vede già l' istanza != null , inizierà a usarla e causerà un problema. Quindi dobbiamo aggiungere volatile all'istanza, quindi il codice diventa così:
class Singleton {
    private Singleton() {}
    private volatile static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Utilizzando una classe interna statica

public class Singleton {
  private Singleton() {}
  private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}
SingletonHolder è una classe interna statica, viene inizializzata solo quando viene chiamato il metodo getInstance . La classe init nella JVM eseguirà <clinit> cmd , quindi la JVM stessa si assicurerà che solo un thread possa chiamare <clinit> sulla classe di destinazione, gli altri thread aspetteranno.

Enum come singleton

public enum EnumSingleton {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public int setValue(int v) {
        this.value = v;
    }
}
Per impostazione predefinita, un'istanza enum è thread-safe, quindi non è necessario preoccuparsi del blocco ricontrollato ed è abbastanza semplice da scrivere.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION