JavaRush /Blog Java /Random-ES /Pausa para el café #143. Clases selladas en Java 17. 4 fo...

Pausa para el café #143. Clases selladas en Java 17. 4 formas de implementar Singleton

Publicado en el grupo Random-ES

Clases selladas en Java 17

Fuente: Codippa En esta publicación veremos las clases selladas, una nueva característica introducida en Java 17, y cómo declararlas y usarlas con ejemplos. Pausa para el café #143.  Clases selladas en Java 17. 4 formas de implementar Singleton - 1Las clases selladas aparecieron por primera vez en Java 15 como una característica de vista previa y luego en Java 16 con el mismo estado. Esta característica se volvió completamente funcional con el lanzamiento de Java 17 ( JEP 409 ).

¿Qué son las clases selladas?

Una clase sellada le permite restringir o seleccionar subclases. Una clase no puede extender una clase privada a menos que esté en la lista de clases secundarias permitidas de la clase principal. La clase se sella utilizando la palabra clave sellada . La clase sellada debe ir seguida de la palabra clave permits , junto con una lista de clases que pueden ampliarla. He aquí un ejemplo:
public sealed class Device permits Computer, Mobile {
}
Esta declaración significa que el Dispositivo sólo puede ser ampliado por las clases Computadora y Móvil . Si alguna otra clase intenta extenderlo, se generará un error del compilador. Una clase que extiende una clase sellada debe tener la palabra clave final , sellada o no sellada en su declaración . Por tanto, tenemos una jerarquía de clases fija. ¿Cómo se relaciona esto con la creación de una clase secundaria?
  1. final significa que no se puede subclasificar más.

  2. sellado significa que necesitamos declarar clases secundarias con permisos .

  3. no sellado significa que aquí terminamos la jerarquía padre-hijo .

Por ejemplo, Computadora permite las clases Computadora portátil y Computadora de escritorio siempre que la Computadora portátil permanezca sin sellar . Esto significa que la computadora portátil se puede ampliar con clases como Apple , Dell , HP, etc.

Los principales objetivos de la introducción de clases selladas son:

  1. Hasta ahora, sólo se podía limitar la extensión de una clase utilizando la palabra clave final . Una clase sellada controla qué clases pueden ampliarla incluyéndolas en la lista permitida.

  2. También permite que la clase controle cuáles de ellas serán sus clases hijas.

Normas

Algunas reglas para recordar al usar clases selladas:
  1. Una clase sellada debe definir clases que puedan extenderla usando permisos . Esto no es necesario si las clases secundarias están definidas dentro de la clase principal como una clase interna.

  2. La clase secundaria debe ser final , sellada o no sellada .

  3. Una clase secundaria permitida debe extender su clase sellada principal.

    Es decir, si la clase A sellada permite la clase B, entonces B debe extender A.

  4. Si la clase sellada está en un módulo, entonces las clases secundarias también deben estar en el mismo módulo, o en el mismo paquete si la clase sellada principal está en un módulo sin nombre.

  5. Sólo las clases directamente permitidas pueden ampliar una clase sellada. Es decir, si A es una clase sellada que permite a B extenderla, entonces B también es una clase sellada que permite a C.

    Entonces C sólo puede extender a B, pero no puede extender directamente a A.

Interfaces selladas

Al igual que las clases selladas, las interfaces también se pueden sellar. Una interfaz de este tipo puede permitir la selección de sus interfaces o clases secundarias, que pueden ampliarla mediante permisos . He aquí un buen ejemplo:
public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
Aquí, la interfaz del Dispositivo permite que las interfaces Electrónica y Física la extiendan y la clase DeviceImpl para su posterior implementación.

Registros sellados

Las clases selladas se pueden usar con entradas introducidas en Java 16. Una entrada no puede extender una clase normal, por lo que solo puede implementar una interfaz privada. Además, la notación implica final . Por lo tanto, una entrada no puede utilizar la palabra clave permits porque no se puede subclasificar. Es decir, sólo existe una jerarquía de un solo nivel con registros. He aquí un ejemplo:
public sealed interface Device permits Laptop {
}
public record Laptop(String brand) implement Device {
}

Apoyo a la reflexión

Java Reflection proporciona soporte para clases selladas. Se han agregado los dos métodos siguientes a java.lang.Class :

1. getPermittedSubclasses()

Esto devuelve una matriz java.lang.Class que contiene todas las clases permitidas por este objeto de clase. Ejemplo:
Device c = new Device();
Class<? extends Device> cz = c.getClass();
Class<?>[] permittedSubclasses = cz.getPermittedSubclasses();
for (Class<?> sc : permittedSubclasses){
  System.out.println(sc.getName());
}
Conclusión:
Computadora Móvil

2.está sellado()

Esto devuelve verdadero si la clase o interfaz en la que se llama está sellada. Eso es todo por ahora sobre las clases selladas agregadas en Java 17. Espero que este artículo haya sido informativo.

4 formas de implementar Singleton

Fuente: Medio Hoy aprenderá varias formas de implementar el patrón de diseño Singleton. El patrón de diseño Singleton se usa ampliamente en proyectos Java. Proporciona control de acceso a recursos, como un socket o una conexión de base de datos. Una vez me pidieron que implementara un singleton durante una entrevista para un puesto de desarrollador web en una gran empresa de chips. Esta fue la primera vez que me entrevisté para un puesto web y no hice mucha preparación, así que elegí la solución más difícil: creación de instancias diferidas. Mi código solo era correcto en un 90% y no era lo suficientemente eficiente, terminé perdiendo en la ronda final... Así que espero que mi artículo te sea útil.

Creación de instancias tempranas

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

    public static Singleton getInstance() {
        return instance;
    }
}
Dado que el objeto ya se creó durante la inicialización, no hay ningún problema de seguridad de subprocesos aquí, pero desperdicia recursos de memoria si nadie lo está usando.

Implementación perezosa

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
Cuando se utiliza un patrón de inicialización diferida, el objeto se crea según demanda. Sin embargo, este método tiene un problema de seguridad de subprocesos: si se inician dos subprocesos en la línea 5 al mismo tiempo, crearán dos instancias Singleton. Para evitar esto, necesitamos agregar un candado:
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;
    }
}
Bloqueo de doble verificación (DCL): no hay bloqueo en la línea 6, por lo que esta línea funcionará muy rápidamente si el objeto ya se ha creado. ¿Por qué necesitamos volver a verificar la instancia == null ? Porque quizás haya dos subprocesos introducidos en la línea 7: el primero inició el objeto, el segundo está esperando el bloqueo Singleton.class . Si no hay verificación, el segundo hilo volverá a crear el objeto singleton. Sin embargo, este método sigue siendo peligroso para los subprocesos. La línea 9 se puede dividir en tres líneas de código de bytes:
  1. Asignar memoria.
  2. Objeto de inicio.
  3. Asignar objeto a referencia de instancia.
Debido a que la JVM puede funcionar fuera de servicio, la máquina virtual puede asignar un objeto a una referencia de instancia antes de la inicialización. Otro hilo ya ve la instancia != null , comenzará a usarla y causará un problema. Entonces necesitamos agregar volátil a la instancia, luego el código queda así:
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;
    }
}

Usando una clase interna estática

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 es una clase interna estática, solo se inicializa cuando se llama al método getInstance . La clase init en la JVM ejecutará <clinit> cmd , luego la propia JVM se asegurará de que solo un subproceso pueda llamar a <clinit> en la clase de destino, otros subprocesos esperarán.

Enum como singleton

public enum EnumSingleton {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public int setValue(int v) {
        this.value = v;
    }
}
De forma predeterminada, una instancia de enumeración es segura para subprocesos, por lo que no hay necesidad de preocuparse por el bloqueo de doble verificación y es bastante fácil de escribir.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION