Translate

Mostrando las entradas con la etiqueta Java. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Java. Mostrar todas las entradas

lunes, 7 de julio de 2025

Mapeo de Herencia en JPA


JPA ofrece 3 estrategias para mapear herencia:

  • @Inheritance(strategy = SINGLE_TABLE) : Todo se guarda en una sola tabla con una columna discriminadora. 
  • @Inheritance(strategy = JOINED): Se usa una tabla por clase, relacionadas por claves foráneas.        
  • @Inheritance(strategy = TABLE_PER_CLASS): Una tabla para cada clase, sin relaciones.                           

Veamos un ejemplo: Clase base y subclases

Vamos a usar la estrategia: SINGLE_TABLE.


package com.ejemplo.demo.entidad;

import jakarta.persistence.*;


@Entity

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

@DiscriminatorColumn(name = "tipo_empleado", discriminatorType = DiscriminatorType.STRING)

public abstract class Empleado {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String nombre;


    // Getters y setters

}


@Inheritance(...): Define la estrategia de herencia.

@DiscriminatorColumn(...):  Crea una columna adicional (tipo_empleado) que almacena el tipo real de cada instancia.


📄 Gerente.java


package com.ejemplo.demo.entidad;


import jakarta.persistence.*;


@Entity

@DiscriminatorValue("GERENTE")

public class Gerente extends Empleado {


    private String departamento;


    // Getters y setters

}


📄 Desarrollador.java


package com.ejemplo.demo.entidad;


import jakarta.persistence.*;


@Entity

@DiscriminatorValue("DESARROLLADOR")

public class Desarrollador extends Empleado {


    private String lenguajeFavorito;


    // Getters y setters

}


Resultado en la base de datos


Se crea una única tabla empleado, con las columnas:

id, nombre, departamento, lenguajeFavorito y tipo_empleado (con valores como GERENTE, DESARROLLADOR)


Ventajas de SINGLE_TABLE

    • Más simple y rápida para consultas.

    • Ideal si la mayoría de los campos son comunes.

Desventajas

    • Muchos campos nulos en columnas que sólo usa una subclase.


Otras estrategias: 

    • JOINED: separa en tablas, y junta usando claves foráneas. Útil si los datos son muy distintos y no querés campos nulos.

    • TABLE_PER_CLASS: evita JOINs, pero no es tan usada. No permite relaciones polimórficas fácilmente.


domingo, 6 de julio de 2025

Relaciones entre Entidades con JPA


Aprender a modelar relaciones entre tablas en Java usando JPA, como por ejemplo:

  • @OneToMany (uno a muchos)

  • @ManyToOne (muchos a uno)

  • @OneToOne (uno a uno)

  • @ManyToMany (muchos a muchos)


En este ejemplo vamos a modelar una relación simple: Una Persona puede tener varias Direcciones.


package com.ejemplo.demo.entidad;


import jakarta.persistence.*;

import java.util.List;


@Entity

public class Persona {


@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String nombre;

@OneToMany(mappedBy = "persona", cascade = CascadeType.ALL, orphanRemoval = true)

private List<Direccion> direcciones;


// Constructores, getters y setters

}


Anotaciones clave:

@OneToMany(mappedBy = "persona"): Declara una relación de uno a muchos. Una persona puede tener varias direcciones.

  • mappedBy = "persona": indica que el atributo que mantiene la relación está del otro lado (Direccion.persona).
  • cascade = CascadeType.ALL: al guardar o eliminar una persona, se aplican los cambios también a sus direcciones.
  • orphanRemoval = true: elimina direcciones si se quitan de la lista.



Entidad Direccion:



package com.ejemplo.demo.entidad;


import jakarta.persistence.*;


@Entity

public class Direccion {


@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String calle;

private String ciudad;


@ManyToOne

@JoinColumn(name = "persona_id")

private Persona persona;


// Constructores, getters y setters

}



Anotaciones clave:

@ManyToOne: Cada dirección pertenece a una sola persona.

@JoinColumn(name = "persona_id"): Crea una columna en la tabla direccion llamada persona_id que actúa como clave foránea a la tabla persona.


Las listas deben ser inicializadas como new ArrayList<>() si se van a usar directamente.

Si no usás orphanRemoval, las direcciones quedan "huérfanas" si quitás una de la lista.


Un pequeño ejemplo para que vayamos incrementando nuestro conocimiento en JPA. 

miércoles, 2 de julio de 2025

¿Qué es Spring Data JPA? Parte 2

 


Veamos un ejemplo de entidad de jpa: 

📄 Persona.java


package com.ejemplo.demo.entidad;

import jakarta.persistence.*;


@Entity

public class Persona {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String nombre;

    private String email;


    // Constructores

    public Persona() {}

    public Persona(String nombre, String email) {

        this.nombre = nombre;

        this.email = email;

    }


    // Getters y setters

    public Long getId() { return id; }

    public String getNombre() { return nombre; }

    public void setNombre(String nombre) { this.nombre = nombre; }

    public String getEmail() { return email; }

    public void setEmail(String email) { this.email = email; }

}


@Entity: Marca esta clase como una entidad JPA, lo que significa que será persistida en una tabla de la base de datos. El nombre de la tabla por defecto será el mismo que el nombre de la clase (persona en minúsculas).

@Id: Indica que el atributo id es la clave primaria de la entidad. Cada fila tendrá un valor único en esta columna.

@GeneratedValue(strategy = GenerationType.IDENTITY):  Especifica que el valor del campo id será generado automáticamente por la base de datos (usualmente como autoincremental).  Existen otras estrategias (AUTO, SEQUENCE, TABLE), pero IDENTITY es simple y efectiva para bases H2 o MySQL.


Repositorio

📄 PersonaRepository.java


package com.ejemplo.demo.repositorio;

import com.ejemplo.demo.entidad.Persona;

import org.springframework.data.jpa.repository.JpaRepository;


public interface PersonaRepository extends JpaRepository<Persona, Long> {

}


JpaRepository<Persona, Long>:  Es una interfaz de Spring Data que nos proporciona métodos CRUD listos para usar: findAll(), save(), deleteById(), etc.

Persona: el tipo de entidad

Long: el tipo de la clave primaria (id)

No se necesita ninguna anotación porque Spring escanea esta interfaz y genera una implementación automática.


Servicio

📄 PersonaService.java


@Service

public class PersonaService {

    // ...

}


@Service:  Marca esta clase como un componente de servicio, lo que permite que Spring la detecte y gestione como parte del contexto de la aplicación (inyección de dependencias, ciclo de vida, etc.).

Controlador

📄 PersonaController.java


@RestController

@RequestMapping("/personas")

public class PersonaController {

    // ...

}


    • @RestController:  Marca la clase como un controlador REST. Es equivalente a usar @Controller + @ResponseBody para cada método.

    • @RequestMapping("/personas"):  Define la ruta base de la API. Todos los métodos se expondrán bajo /personas.

    • @GetMapping, @PostMapping, @DeleteMapping: Indican qué método HTTP debe usarse para cada operación.

    • @RequestBody:  Indica que los datos del cuerpo del request (formato JSON) deben ser convertidos a un objeto Java.

    • @PathVariable: Extrae una variable de la URL, por ejemplo /personas/1 extrae el id = 1.


Archivo application.properties

spring.datasource.url=jdbc:h2:mem:testdb

spring.jpa.hibernate.ddl-auto=update

spring.h2.console.enabled=true


spring.jpa.hibernate.ddl-auto=update:  Le indica a JPA que genere automáticamente las tablas según nuestras entidades. Si la tabla no existe, la crea.


martes, 1 de julio de 2025

¿Qué es Spring Data JPA?


JPA (Java Persistence API) es una especificación estándar de Java para el mapeo objeto-relacional (ORM), que permite interactuar con bases de datos relacionales utilizando objetos Java.


Con JPA se pueden:

    • Definir clases Java como entidades persistentes (@Entity).

    • Especificar relaciones entre entidades (uno a muchos, muchos a muchos, etc.).

    • Realizar operaciones como insertar, actualizar, eliminar y consultar datos sin escribir SQL explícito.


JPA no es una implementación, sino una interfaz estándar. Las implementaciones más comunes son:

    • Hibernate (la más usada)

    • EclipseLink

    • OpenJPA


Spring Data JPA es un módulo de Spring que simplifica el uso de JPA:

    • Permite definir interfaces de repositorio sin necesidad de implementar los métodos CRUD.

    • Integra la capa de persistencia con el resto del ecosistema de Spring (inyección de dependencias, control transaccional, validaciones, etc.).

    • Facilita la creación de consultas con nombres de método (findByNombre, findByEmailContaining, etc.).

    • Soporta JPQL, SQL nativo y consultas dinámicas.


Spring Data JPA es una abstracción sobre JPA que reduce drásticamente el código necesario para acceder a la base de datos.


Ventajas de usar JPA con Spring

    • Menos código repetitivo: no es necesario implementar manualmente métodos CRUD.

    • Integración total con Spring: todo se gestiona mediante inyección de dependencias y configuración automática.

    • Consultas declarativas: se pueden crear consultas complejas usando el nombre del método.

    • Abstracción del SQL: permite trabajar con objetos sin escribir sentencias SQL directamente.

    • Gestión automática del contexto de persistencia: Spring gestiona transacciones, apertura y cierre de conexiones.

    • Compatible con múltiples bases de datos: MySQL, PostgreSQL, H2, Oracle, SQL Server, etc.


En el siguiente post vamos con algo más práctico. 


jueves, 19 de junio de 2025

Java 25 integra Compact Object Headers (JEP 519)

 


Java 25, la próxima versión LTS, incorpora de forma nativa el soporte para Compact Object Headers mediante la JEP 519. Esta característica, que era experimental en Java 24, ahora se promueve como una funcionalidad oficial del VM, lo que implica grandes beneficios sin necesidad de modificar el código existente.

¿Qué son los Compact Object Headers?

En HotSpot (el JVM de referencia), cada objeto en memoria tiene un cabecera (header) que contiene meta información:

  • Un mark word (incluye bits para GC, monitoreo y hashcode)
  • Un class word (referencia comprimida a la clase del objeto)

Hasta ahora, este header ocupa 96 bits (12 bytes), lo cual significa un sobrecoste significativo —sobre todo en aplicaciones con muchos objetos de pequeño tamaño.


¿Qué cambia con Compact Object Headers?


Reducción del header a 64 bits (8 bytes), manteniendo todo lo necesario:

  • Mark word
  • Class pointer (comprimido a 22 bits)
  • Campos reservados para GC y futuros proyectos como Valhalla 

El ahorro en memoria puede llegar al 30 % de CPU y 20 % menos uso de heap en aplicaciones que crean muchos objetos pequeños. Benchmarks como SPECjbb2015 muestran mejoras de 22 % en heap y 8 % en CPU, además de menor frecuencia de GC.

Amazon ya lo ha probado en centenas de servicios, validando caídas de hasta 30 % en consumo CPU y 15 % menos GC, especialmente en colectores G1 y Parallel.


¿Cómo habilitarlo?


En Java 25 basta con agregar el siguiente flag al iniciar tu aplicación:


java -XX:+UseCompactObjectHeaders -jar mi-app.jar


Sin necesidad del flag -XX:+UnlockExperimentalVMOptions, ni cambiar una sola línea de código 


¿Por qué es relevante?

  • Menor consumo de memoria: ideal para microservicios, Kubernetes, contenedores o entornos edge.
  • Mejor rendimiento: los objetos más pequeños mejoran la localidad de caché y reducen cargas de GC.
  • Implementación sencilla: es transparente y compatible con código existente, sin riesgo ni esfuerzo extra.


Compact Headers es parte de Project Lilliput, cuyo objetivo es reducir drásticamente el overhead de los headers de objetos:

  • JDK 22 introdujo estructura para monitores de objetos
  • JEP 450 en Java 24 activó Compact Headers en modo experimental
  • Ahora, JEP 519 en Java 25 los integra como funcionalidad oficial 


Esto también adelanta compatibilidad con futuras mejoras como Valhalla (clases de valor) y Project Panama.


Recomendaciones prácticas

  • Activá en entornos de desarrollo o staging y monitorizá el uso de memoria y CPU.
  • En entornos de producción, el flag puede reducir costes operativos y mejorar densidad en la nube.
  • Revisá que no actives `-XX:-UseCompressedClassPointers` o locking legado, ya obsoletos en Java 25.
  • Esperá mejoras similares en plataformas x64 y AArch64; ZGC en x64 aún está en progreso


Java 25 (LTS) ofrece ahora una forma sencilla de optimizar memoria y rendimiento con un solo parámetro. Compact Object Headers representan una mejora tangible para cualquier proyecto moderno que genere muchos objetos. Es una mejora que conviene activar y medir desde ya.

Dejo link: 

https://www.infoq.com/news/2025/06/java-25-compact-object-headers/


jueves, 8 de mayo de 2025

Beans Singleton en Spring: ¿Son un riesgo en entornos concurrentes?


Cuando trabajamos con Spring Framework, una de las primeras cosas que aprendemos es que los beans por defecto son singleton. Es decir, Spring crea una única instancia de cada bean y la reutiliza a lo largo de toda la aplicación. Ojo aca, no es igual que el patron singleton, porque el patron singleton es una instancia por clase. Spring realiza una instancia por bean, pero varios beans pueden ser de la misma clase. 

Pero de igual manera nos podemos preguntar ¿No podría causar problemas si múltiples hilos usan el mismo objeto?

Veamos por qué no, y en qué casos sí.

Cuando marcamos una clase con @Component, @Service, @Repository, etc., sin indicar un @Scope, Spring crea una única instancia de esa clase cuando arranca el contexto, y luego la reutiliza para inyectarla donde haga falta.


@Service

public class MiServicio {

    public void hacerAlgo() {

        // lógica

    }

}

Este bean se compartirá entre todas las partes de la aplicación que lo necesiten.

¿Y si lo usan varios hilos?

Acá es donde entra el concepto de thread safety. Si el bean no guarda estado mutable (es stateless), no hay ningún problema. Puede ser accedido por múltiples hilos al mismo tiempo sin consecuencias.

Pero si el bean mantiene estado mutable (por ejemplo, una variable de instancia que se modifica en cada método), entonces puede haber condiciones de carrera, errores y comportamiento impredecible.

Veamos un ejemplo peligroso:


@Component

public class ContadorCompartido {

    private int contador = 0;


    public void incrementar() {

        contador++;

    }


    public int getContador() {

        return contador;

    }

}


Este bean no es thread-safe: si lo usan múltiples hilos al mismo tiempo, el contador puede tener resultados inesperados.

Otra pregunta puede ser ¿Spring crea múltiples instancias si hay mucha carga?

No. Este es un mito común. Spring no crea múltiples instancias de un bean singleton automáticamente bajo carga. La única forma en que podrías tener más de una instancia es:

  • Si usamos un @Scope("prototype"), @RequestScope, etc.
  • Si instanciás manualmente objetos sin pasar por el contenedor de Spring (lo cual rompe la inyección de dependencias).
  • O si usás proxies o mecanismos especiales para aislar contexto por request, como ocurre con algunos beans en entornos web.


Podemos concluir que : 

  • Si tu bean necesita guardar estado por usuario o por request, no lo hagas singleton. Usá un scope adecuado.
  • Si tu bean puede ser stateless, mantenelo así. Es más simple, más eficiente y seguro en entornos multihilo.
  • Si el bean debe tener estado compartido, usá sincronización o mecanismos thread-safe como AtomicInteger, ConcurrentHashMap, etc.

En Spring, los beans singleton son una gran herramienta para reutilización de lógica y eficiencia de memoria. Pero es tu responsabilidad asegurarte de que no mantengan estado mutable que pueda generar conflictos en concurrencia.

martes, 22 de abril de 2025

Tipos Genéricos Anónimos en Scala: Wildcards y Subtipado


Antes de empezar este post tal vez sería bueno que leas este post antes :D

Scala permite definir colecciones y estructuras de datos genéricas, pero a veces no necesitamos saber con precisión qué tipo contienen, o simplemente queremos permitir varios tipos relacionados. Para esos casos existen los tipos genéricos anónimos, representados por el underscore (_).

Un tipo genérico anónimo en Scala se escribe con un guion bajo (_) en lugar de especificar un tipo concreto. Es útil cuando:

  • No necesitás conocer el tipo exacto.
  • Queremos aceptar varios subtipos o supertypos.

Veamos un ejemplo:

Lista de cualquier tipo (wildcard total)


val lista: List[_] = List(1, "hola", true)


Esto indica que lista es de algún tipo List[T], pero no importa cuál es T.


Subtipado: _ <: Tipo

Permite aceptar cualquier subtipo del tipo dado (covarianza).


class Animal

class Perro extends Animal

class Gato extends Animal


val animales: List[_ <: Animal] = List(new Perro, new Gato)


Esto significa: una lista de algo que es subtipo de Animal.


Supertyping: _ >: Tipo

Permite aceptar cualquier supertipo del tipo dado (contravarianza).


val cosas: List[_ >: Perro] = List(new Animal, new Perro)


Esto significa: una lista de algo que es supertipo de Perro.


Y ¿Por qué usar genéricos anónimos?

  • Cuando escribís funciones genéricas que pueden aceptar muchos tipos.
  • Para asegurar compatibilidad con estructuras covariantes/contravariantes.
  • Para restringir o abrir el tipo de manera controlada.

Los tipos anónimos no te permiten hacer mucho con los elementos (no podés acceder a sus métodos específicos), porque Scala no sabe exactamente qué tipo hay.


val lista: List[_] = List("hola", "chau")

// lista(0).toUpperCase()  // ERROR: no se puede garantizar el tipo


En Java existe el signo de pregunta para esto (?):


List<?> lista;

List<? extends Animal> subtipos;

List<? super Perro> supertypos;


En Scala es más limpio y expresivo:


List[_]

List[_ <: Animal]

List[_ >: Perro]


Los tipos genéricos anónimos en Scala te permiten trabajar con estructuras de datos más genéricas y flexibles, especialmente en APIs o librerías donde no se necesita o no se conoce el tipo exacto. Son ideales para mantener la seguridad de tipos sin perder generalidad.


viernes, 11 de abril de 2025

JEP 463 : Clases Implícitamente Declaradas y Métodos main de Instancia

La Propuesta de Mejora de Java (JEP) 463, titulada "Clases Implícitamente Declaradas y Métodos main de Instancia", introduce en Java 22 una funcionalidad que simplifica la escritura de programas básicos, especialmente útil para principiantes en el lenguaje.

El propósito principal de esta propuesta es permitir que los estudiantes y nuevos programadores escriban sus primeros programas en Java sin necesidad de comprender características avanzadas del lenguaje. Esto se logra permitiendo la declaración implícita de clases y la utilización de métodos main de instancia, eliminando la necesidad de especificar modificadores como public, static y el parámetro String[] args.

Las características Principales son: 

1. Clases Implícitamente Declaradas: Permiten escribir programas sin declarar explícitamente una clase. El compilador asume una clase implícita que envuelve el código proporcionado.


   void main() {

       System.out.println("Hola, mundo!");

   }


En este caso, no es necesario declarar una clase explícita; el compilador lo maneja automáticamente. 

2. Métodos main de Instancia: Permiten que el método main sea un método de instancia en lugar de un método estático. Esto facilita la introducción de conceptos orientados a objetos de manera más natural para los principiantes.


   void main() {

       System.out.println("Hola desde un método main de instancia!");

   }



Aquí, el método main no requiere ser estático, lo que simplifica la estructura del programa inicial. 

Como beneficios podemos nombrar:

  • Simplicidad: Reduce la cantidad de código ceremonial necesario para escribir programas simples, facilitando el aprendizaje inicial de Java.
  • Progresión Natural: Permite a los estudiantes comenzar con estructuras simples y luego introducir gradualmente conceptos más complejos como clases y métodos estáticos.
  • Compatibilidad: Mantiene la compatibilidad con las características existentes de Java, permitiendo una transición suave hacia programas más complejos.

Es importante destacar que esta funcionalidad se introdujo como una característica en vista previa en Java 21 bajo la JEP 445 y se reintrodujo en Java 22 con la JEP 463, incorporando mejoras basadas en la retroalimentación recibida. Como característica en vista previa, está sujeta a cambios en futuras versiones del lenguaje. 

La JEP 463 representa un avance significativo en la accesibilidad del lenguaje Java para nuevos programadores, simplificando la estructura necesaria para escribir programas básicos y facilitando una curva de aprendizaje más amigable. Al permitir la declaración implícita de clases y métodos main de instancia, Java se vuelve más accesible sin comprometer su robustez para desarrollos más complejos.

Dejo link: https://openjdk.org/jeps/463 

viernes, 4 de abril de 2025

JEP 459: Plantillas de Cadenas en Java


La propuesta de Mejora de Java (JEP) 459, titulada "Plantillas de Cadenas", introduce en Java una nueva funcionalidad que permite la creación de cadenas dinámicas de manera más sencilla y segura. Esta característica, presentada como vista previa en Java 22, complementa las literales de cadena y los bloques de texto existentes al combinar texto literal con expresiones embebidas y procesadores de plantillas para producir resultados especializados. 

Objetivos de JEP 459

  • Simplificar la escritura de programas Java: Facilita la expresión de cadenas que incluyen valores calculados en tiempo de ejecución.
  • Mejorar la legibilidad: Permite una sintaxis más clara al mezclar texto y expresiones, ya sea en una sola línea o en múltiples líneas.
  • Incrementar la seguridad: Al componer cadenas a partir de valores proporcionados por el usuario, se mejora la validación y transformación de las plantillas y sus valores embebidos.
  • Mantener flexibilidad: Permite que las bibliotecas de Java definan la sintaxis de formato utilizada en las plantillas de cadenas.
  • Simplificar el uso de APIs: Facilita la interacción con APIs que aceptan cadenas escritas en lenguajes no Java, como SQL, XML y JSON. 


Las plantillas de cadenas permiten insertar expresiones directamente dentro de cadenas de texto utilizando una sintaxis específica. A continuación, se muestra un ejemplo básico de su uso:


String nombre = "Duke";

String mensaje = STR."Hola, \{nombre}!";

System.out.println(mensaje);


En este ejemplo, `STR` es un procesador de plantillas que evalúa la expresión embebida `\{nombre}` y la reemplaza con su valor correspondiente, produciendo la salida: "Hola, Duke!". 

Java 22 incorpora varios procesadores de plantillas, entre los que se incluyen:

  • STR: Realiza interpolación de cadenas, reemplazando cada expresión embebida con su valor correspondiente convertido a cadena.
  • FMT: Similar a STR, pero acepta especificadores de formato como los definidos en java.util.Formatter, permitiendo un formateo más detallado.
  • RAW`: No procesa automáticamente la plantilla de cadena, útil para crear procesadores de plantillas personalizados.

Es fundamental tener en cuenta que las plantillas de cadenas fueron introducidas como una funcionalidad en vista previa en Java 21 y reintroducidas en Java 22 con JEP 459. Sin embargo, esta característica fue retirada en Java 23 para una revisión y rediseño adicionales, por lo que no está disponible en versiones posteriores.

Aunque actualmente esta característica está en revisión, su implementación futura promete simplificar y asegurar la construcción de cadenas en Java, facilitando la interacción con otros sistemas y mejorando la legibilidad del código.


Dejo link: https://openjdk.org/jeps/459

jueves, 3 de abril de 2025

JEP 456: Variables y Patrones Anónimos en Java



La Propuesta de Mejora de Java (JEP) 456, titulada "Variables y Patrones Anónimos", introduce en Java la capacidad de declarar variables y patrones sin nombre utilizando el carácter de subrayado (_). Esta característica, incorporada en Java 22, permite a los desarrolladores indicar explícitamente que ciertas variables o parámetros no serán utilizados, mejorando así la claridad y mantenibilidad del código. 

En situaciones donde una variable debe ser declarada pero su valor no es necesario, se puede emplear el carácter _ como nombre de la variable. Esto es útil en contextos como bucles for, bloques catch y expresiones lambda.

Veamos un ejemplo en un bucle for:


int[] numeros = {1, 2, 3, 4, 5};

int total = 0;

for (int _ : numeros) {

    total++;

}

System.out.println("Total: " + total);


En este caso, el bucle itera sobre el array numeros sin necesidad de utilizar el valor de cada elemento, incrementando simplemente el contador total.

Veamos un ejemplo en un bloque catch:


try {

    // Código que puede lanzar una IOException

} catch (IOException _) {

    System.out.println("Ocurrió un error de E/S.");

}


Aquí, se captura la excepción IOException sin necesidad de referenciarla dentro del bloque catch. 

Los patrones anónimos permiten omitir nombres y tipos en patrones de registros cuando no se requiere acceder a ciertos componentes. Esto simplifica la sintaxis y mejora la legibilidad del código al desestructurar objetos.


record Punto(int x, int y) {}

Punto punto = new Punto(5, 10);


if (punto instanceof Punto(int _, int y)) {

    System.out.println("Coordenada y: " + y);

}


En este ejemplo, se omite el nombre de la variable para la coordenada x, ya que no es necesaria, mientras que se accede directamente a la coordenada y. 

La introducción de variables y patrones anónimos en Java 22 representa un avance significativo hacia una sintaxis más concisa y expresiva, alineada con las necesidades modernas de desarrollo.

martes, 1 de abril de 2025

Novedades de java 22


¡JDK 22 ya está disponible!

JDK 22 ofrece 12 mejoras significativas. Estas mejoras abarcan mejoras en el lenguaje Java, sus API, su rendimiento y las herramientas incluidas en el JDK.

1) Mejoras del lenguaje:

  • Variables y patrones sin nombre - JEP 456: Mejora la legibilidad cuando se requieren declaraciones de variables o patrones anidados, pero no se utilizan. Ambos se indican con el guión bajo.

1.1) Vistas preview del lenguaje

  • Declaraciones antes de super(…) - JEP 447: En los constructores, permite que las declaraciones que no hacen referencia a la instancia que se está creando aparezcan antes de una invocación explícita del constructor.
  • Plantillas de cadena (Segunda versión preliminar) - JEP 459: Las plantillas de cadena complementan los literales de cadena y los bloques de texto existentes de Java al combinar el texto literal con expresiones integradas y procesadores de plantillas para producir resultados especializados.
  • Clases declaradas implícitamente y métodos principales de instancia (Segunda vista previa) - JEP 463: Los estudiantes pueden escribir sus primeros programas en Java sin necesidad de comprender las características del lenguaje diseñadas para programas grandes. En lugar de usar un dialecto independiente del lenguaje, pueden escribir declaraciones simplificadas para programas de una sola clase y luego ampliarlos sin problemas para usar funciones más avanzadas a medida que mejoran sus habilidades.


2) Bibliotecas

  • API de Funciones Externas y Memoria - JEP 454: Permite que los programas Java interoperen con código y datos fuera del entorno de ejecución de Java. Al invocar eficientemente funciones externas (es decir, código fuera de la JVM) y acceder de forma segura a la memoria externa (es decir, memoria no gestionada por la JVM), la API permite que los programas Java invoquen bibliotecas nativas y procesen datos nativos sin la fragilidad ni los riesgos de JNI.


2.1) Vistas previas de bibliotecas e incubadora

  • API de archivo de clase (Vista previa) - JEP 457: Proporciona una API estándar para analizar, generar y transformar archivos de clase Java.
  • Recolectores de streams (Vista previa) - JEP 461: Mejora la API de streams para admitir operaciones intermedias personalizadas. Esto permitirá que las canalizaciones de flujos transformen datos de maneras que no son fáciles de lograr con las operaciones intermedias integradas existentes.
  • Concurrencia estructurada (2.ª vista previa) - JEP 462: Simplifica la programación concurrente. La concurrencia estructurada trata grupos de tareas relacionadas que se ejecutan en diferentes hilos como una sola unidad de trabajo, optimizando así la gestión y cancelación de errores, mejorando la fiabilidad y la observabilidad.
  • Valores con Alcance (2.ª Vista Previa) - JEP 464: Permite compartir eficientemente datos inmutables dentro y entre subprocesos.
  • API Vector (7.ª Incubadora) - JEP 460: Una API para expresar cálculos vectoriales que se compilan de forma fiable en tiempo de ejecución para obtener instrucciones vectoriales óptimas en arquitecturas de CPU compatibles, logrando así un rendimiento superior al de cálculos escalares equivalentes. Este JEP propone reincubar la API en JDK 22, con mejoras menores en la API con respecto a JDK 21. La implementación incluye correcciones de errores y mejoras de rendimiento. Incluimos los siguientes cambios notables: Compatibilidad con el acceso vectorial con segmentos de memoria del montón respaldados por un array de cualquier tipo de elemento primitivo. Anteriormente, el acceso estaba limitado a segmentos de memoria del montón respaldados por un array de bytes.


3) Rendimiento

  • Fijación regional para G1 - JEP 423: Reduce la latencia al implementar la fijación regional en G1, de modo que no es necesario deshabilitar la recolección de elementos no utilizados durante las regiones críticas de la Interfaz Nativa de Java (JNI).


4) Herramientas

Iniciar programas de código fuente con múltiples archivos - JEP 458: Permite a los usuarios ejecutar un programa proporcionado como múltiples archivos de código fuente Java sin tener que compilarlo primero.


Java 22 continúa la evolución del lenguaje, incorporando mejoras que aumentan su expresividad, rendimiento y seguridad. Estas actualizaciones refuerzan el compromiso de la plataforma con la innovación y la adaptabilidad a las necesidades actuales de desarrollo.

En proximos post vamos a detallar las mejoras del lenguaje. 

Dejo link: https://blogs.oracle.com/java/post/the-arrival-of-java-22

lunes, 24 de marzo de 2025

Diferencias entre @Component, @Controller, @Service y @Repository en Spring Boot


Spring Boot proporciona varias anotaciones para marcar clases como beans dentro del contenedor de Spring. Entre ellas, @Component, @Controller, @Service y @Repository son las más utilizadas. Aunque todas registran un bean dentro del contexto de Spring, cada una tiene un propósito específico. A continuación, explicamos sus diferencias y usos recomendados.

@Component es la anotación más genérica y sirve para marcar cualquier clase como un bean de Spring. Cualquier clase anotada con @Component será detectada automáticamente por el escaneo de componentes de Spring y registrada en el contexto de la aplicación.


@Component

public class MiComponente {

    public void hacerAlgo() {

        System.out.println("Ejecutando lógica en MiComponente");

    }

}

Se recomienda usar @Component cuando la clase no encaje específicamente en @Controller, @Service o @Repository.


@Controller es una especialización de @Component que se utiliza en el contexto de Spring MVC para manejar solicitudes HTTP. Las clases anotadas con`@Controller se encargan de procesar peticiones web y devolver respuestas.


@Controller

public class MiControlador {

    @GetMapping("/saludo")

    public String saludo(Model model) {

        model.addAttribute("mensaje", "¡Hola desde el controlador!");

        return "saludo";

    }

}


Si el controlador necesita devolver solo datos en formato JSON o XML, se recomienda usar @RestController, que combina`@Controller y @ResponseBody.


@Service también es una especialización de @Component, pero se utiliza para indicar que una clase contiene lógica de negocio. Su uso es más semántico, ayudando a la organización y mantenimiento del código.


@Service

public class MiServicio {

    public String obtenerSaludo() {

        return "Hola desde el servicio";

    }

}


Se recomienda usar @Service para clases que encapsulen lógica de negocio y sean reutilizables en distintos lugares de la aplicación.


@Repository es otra especialización de @Component, pero está orientada a la capa de acceso a datos. Además de marcar la clase como un bean, @Repository proporciona integración con mecanismos de persistencia, como la gestión automática de excepciones relacionadas con bases de datos.


@Repository

public interface MiRepositorio extends JpaRepository<Entidad, Long> {

}

Spring usa @Repository para traducir excepciones de bases de datos en excepciones no verificadas de Spring (DataAccessException).


Cada anotación tiene su uso específico y contribuye a mantener una arquitectura limpia y organizada. Elegir la anotación adecuada ayuda a mejorar la mantenibilidad y escalabilidad de la aplicación.


martes, 11 de marzo de 2025

Clases sin Nombre y Métodos Principales sin Nombre en Java 21


Con Java 21, se introducen dos características que simplifican el código para desarrolladores: clases sin nombre y métodos main sin nombre. Estas mejoras buscan reducir la cantidad de código boilerplate en programas simples y facilitar el aprendizaje de Java para nuevos desarrolladores.

Las clases sin nombre permiten escribir código sin necesidad de definir explícitamente una clase contenedora. Esto facilita la escritura de programas pequeños o scripts sin la sobrecarga de definir clases innecesarias.

Antes de Java 21, un programa en Java requería una clase con un main:


public class MiClase {

    public static void main(String[] args) {

        System.out.println("Hola, Java 21!");

    }

}


Con las clases sin nombre, ahora puedes escribir simplemente:


System.out.println("Hola, Java 21!");


En Java 21, no es obligatorio definir explícitamente public static void main(String[] args). Ahora el código dentro del archivo fuente se ejecuta directamente.


System.out.println("Ejecutando sin un método 'main' explícito!");


¿Cómo funciona?

  • La JVM reconoce el archivo como ejecutable sin necesidad de una clase o main.
  • Útil para scripts rápidos o pruebas sencillas.
  • Sigue siendo posible definir clases si se necesita una estructura más compleja.


Las clases sin nombre y los métodos main sin nombre en Java 21 son pasos importantes para hacer el lenguaje más accesible y fácil de usar en escenarios rápidos y educativos. Si bien Java sigue siendo un lenguaje de propósito general con una fuerte orientación hacia aplicaciones empresariales, estas mejoras lo acercan más al paradigma de scripting y programación ligera, pero a mi entender nos alejan de los conceptos puros de programación orientada a objetos. 


lunes, 10 de marzo de 2025

Spring Native: La Evolución de las Aplicaciones Spring en un Mundo de Desempeño Nativo


Spring Native es un proyecto dentro del ecosistema Spring que permite compilar aplicaciones Spring Boot directamente a imágenes nativas utilizando GraalVM. Esto significa que, en lugar de ejecutar la aplicación en la JVM (Java Virtual Machine), el código se compila en un binario nativo específico de la plataforma.

¿Por qué es importante Spring Native? Como sabran, spring es muy bueno pero pero con la inyección de dependencia y el uso de proxies con lleva a un mayor uso de memoria y a un inicio de aplicación lento. Además que con docker tampoco es una ventaja ser multiplataforma. Con que funcione en docker ya esta. Todos estos problemas son resueltos con Graalvm y spring native. 

Entonces podemos nombras los siguientes beneficios:

  • Rendimiento Mejorado: Las aplicaciones nativas tienen tiempos de inicio mucho más rápidos y un consumo de memoria significativamente menor en comparación con las aplicaciones que corren sobre la JVM. Esto es crucial para aplicaciones en contenedores (como Docker) y para arquitecturas de microservicios donde la eficiencia es clave.
  • Despliegue más Eficiente: El código nativo puede simplificar la infraestructura de despliegue, ya que no depende de tener una JVM instalada. Esto puede ser muy útil en entornos donde el espacio y los recursos son limitados.
  • Menor Consumo de Memoria: Las aplicaciones nativas pueden ejecutarse con un consumo de memoria más bajo, lo que hace que sean ideales para entornos de alta escala, como la nube y la ejecución en dispositivos con recursos limitados.
  • Reducción de la Complejidad Operacional: Al generar una imagen nativa, el proceso de despliegue es más directo y menos dependiente de las complejidades de la configuración de la JVM. Esto puede mejorar la mantenibilidad y reducir los errores de configuración.
  • Mejor Compatibilidad con el Ecosistema de Contenedores: Las imágenes nativas de Spring Boot se integran de forma más eficiente con contenedores como Docker, al permitirles ser más livianas y rápidas al arrancar, lo cual es vital en escenarios donde el escalado rápido y la flexibilidad son necesarios.

Cuando usar Spring Native?: 

  • Microservicios: Dado que las aplicaciones nativas tienen tiempos de inicio rápidos y un menor uso de memoria, son perfectas para entornos de microservicios donde el arranque rápido y el escalado eficiente son esenciales.
  • Sistemas Serverless: Al ser aplicaciones de bajo consumo, pueden integrarse bien con arquitecturas serverless, donde el costo de recursos y el tiempo de ejecución son factores clave.
  • IoT y Dispositivos con Recursos Limitados: En aplicaciones que se ejecutan en dispositivos con recursos limitados, como IoT, la eficiencia de Spring Native puede ser un gran beneficio.


Desafíos y Limitaciones:

  • Compatibilidad: No todas las bibliotecas de Spring Boot son totalmente compatibles con GraalVM, y algunas características pueden requerir ajustes adicionales.
  • Tiempo de compilación: La creación de la imagen nativa puede ser lenta, lo que podría no ser ideal para todos los entornos de desarrollo.


¿Cómo Integrar Spring Native en tu Proyecto?

  • Requisitos: Tener un proyecto Spring Boot básico.
  • Configuración de GraalVM: Instalar GraalVM y configurar el entorno de desarrollo para soportar la compilación nativa.
  • Incluir dependencias nativas: Agregar la dependencia de spring-native en el pom.xml o `build.gradle`.
  • Compilación: Usar `mvn  spring-boot:build-image o ./gradlew buildNative para generar la imagen nativa.
  • Despliegue y pruebas: Probar la imagen nativa generada en el entorno de producción.


Spring Native permite a las aplicaciones Spring aprovechar los beneficios del código nativo: tiempos de arranque rápidos, menor uso de memoria y mayor eficiencia en la ejecución. A medida que las arquitecturas modernas se mueven hacia entornos de microservicios y despliegues en la nube, Spring Native se posiciona como una herramienta clave para la optimización de aplicaciones Spring.


viernes, 7 de marzo de 2025

API de Vectores en Java: Alto Rendimiento con Operaciones SIMD


Java ha introducido mejoras significativas en el rendimiento con la API de Vectores, una funcionalidad experimental desde Java 16 y evolucionada en Java 19 y 20. Esta API permite aprovechar las capacidades de SIMD (Single Instruction, Multiple Data), lo que mejora la eficiencia en cálculos intensivos, como gráficos, procesamiento de señales y machine learning.

La API de Vectores en Java permite realizar operaciones con múltiples valores en paralelo utilizando instrucciones SIMD, optimizando así el uso del hardware subyacente. Está diseñada para ejecutarse de manera más eficiente en CPU modernas con conjuntos de instrucciones avanzados como AVX y NEON.

Las características principales son:

  • Operaciones eficientes con vectores: Realiza cálculos numéricos sobre múltiples datos simultáneamente.
  • Compatibilidad con JVM y JIT: Se integra con el compilador Just-In-Time para optimizar el rendimiento.
  • Optimización automática: Se adapta a la arquitectura del procesador para aprovechar el mejor conjunto de instrucciones disponible.
  • API expresiva y segura: Usa un enfoque declarativo y sin riesgo de desbordamiento de memoria.


Veamos un ejemplo: 


import jdk.incubator.vector.FloatVector;

import jdk.incubator.vector.VectorSpecies;


public class VectorExample {

    static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;


    public static void main(String[] args) {

        float[] a = {1.0f, 2.0f, 3.0f, 4.0f};

        float[] b = {5.0f, 6.0f, 7.0f, 8.0f};

        float[] result = new float[a.length];


        var va = FloatVector.fromArray(SPECIES, a, 0);

        var vb = FloatVector.fromArray(SPECIES, b, 0);

        var vc = va.add(vb);

        vc.intoArray(result, 0);


        System.out.println("Resultado: ");

        for (float v : result) {

            System.out.print(v + " ");

        }

    }

}


  • Se define una especie de vector (SPECIES_PREFERRED) que se ajusta a la arquitectura del CPU.
  • Se cargan los datos en vectores FloatVector.
  • Se realiza una operación de suma en paralelo.
  • Se almacenan los resultados en un array de salida.


En Java 19 y 20 se continuo trabajando obteniendo estas mejoras:

  • Mayor estabilidad y optimización en JVM: Se han reducido las sobrecargas en JIT.
  • Soporte extendido para más arquitecturas: Ahora es compatible con ARM y RISC-V.
  • Nuevas operaciones matemáticas: Más funciones avanzadas como cálculos trigonométricos y logarítmicos.


La API de Vectores en Java es una gran incorporación para quienes necesitan alto rendimiento en operaciones matemáticas y científicas. Con el soporte mejorado en arquitecturas modernas y una mayor optimización en la JVM, esta API se perfila como una herramienta clave para el desarrollo de aplicaciones de alto rendimiento.

miércoles, 5 de marzo de 2025

Cómo Spring Implementa AOP


Spring AOP se basa en el Patrón Proxy y la generación dinámica de clases para aplicar aspectos sin modificar el código fuente original.

Spring AOP crea proxies para interceptar llamadas a métodos y aplicar lógica adicional. Dependiendo de la estructura de la clase objetivo, Spring elige entre dos enfoques:

JDK Dynamic Proxies: Si la clase implementa una interfaz, Spring usa java.lang.reflect.Proxy para generar un proxy en tiempo de ejecución.

CGLIB (Code Generation Library): Si la clase no implementa interfaces, se genera una subclase dinámica con CGLIB.


Veamos un ejemplo de un proxy con JDK:


import java.lang.reflect.*;


interface Servicio {

    void ejecutar();

}


class ServicioImpl implements Servicio {

    public void ejecutar() {

        System.out.println("Ejecutando servicio...");

    }

}


class ProxyHandler implements InvocationHandler {

    private final Object target;

    public ProxyHandler(Object target) {

        this.target = target;

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("[AOP] Antes de ejecutar");

        Object result = method.invoke(target, args);

        System.out.println("[AOP] Después de ejecutar");

        return result;

    }

}


public class Main {

    public static void main(String[] args) {

        Servicio servicio = (Servicio) Proxy.newProxyInstance(

            Servicio.class.getClassLoader(),

            new Class[]{Servicio.class},

            new ProxyHandler(new ServicioImpl()));

        servicio.ejecutar();

    }

}


Si la clase no implementa interfaces, Spring usa CGLIB para generar una subclase dinámica.


import net.sf.cglib.proxy.*;


class Servicio {

    public void ejecutar() {

        System.out.println("Ejecutando servicio...");

    }

}


class Interceptor implements MethodInterceptor {

    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

        System.out.println("[AOP] Antes de ejecutar");

        Object result = proxy.invokeSuper(obj, args);

        System.out.println("[AOP] Después de ejecutar");

        return result;

    }

}


public class Main {

    public static void main(String[] args) {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(Servicio.class);

        enhancer.setCallback(new Interceptor());

        Servicio proxy = (Servicio) enhancer.create();

        proxy.ejecutar();

    }

}


Spring AOP aplica aspectos mediante proxies dinámicos. Si la clase tiene una interfaz, usa JDK Dynamic Proxies; si no, usa CGLIB para generar una subclase dinámica. Esto permite agregar lógica sin modificar el código fuente ni el bytecode.



viernes, 28 de febrero de 2025

API de Servidor Web Simple en Java



A partir de Java 18, se introdujo una API de Servidor Web Simple que permite crear servidores HTTP ligeros sin necesidad de dependencias externas. Esta funcionalidad es ideal para pruebas rápidas, prototipos y aplicaciones livianas.

Como características principales podemos nombrar:

  • Servidor HTTP embebido sin necesidad de librerías adicionales.
  • Manejo de solicitudes GET y POST.
  • Facilidad de uso con pocas líneas de código.
  • Soporte para configuración de rutas y respuestas personalizadas.


Veamos un hola mundo : 


import com.sun.net.httpserver.HttpServer;

import com.sun.net.httpserver.HttpHandler;

import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;

import java.io.OutputStream;

import java.net.InetSocketAddress;


public class SimpleWebServer {

    public static void main(String[] args) throws IOException {

        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

        server.createContext("/hello", new HelloHandler());

        server.setExecutor(null);

        server.start();

        System.out.println("Servidor iniciado en http://localhost:8080/");

    }


    static class HelloHandler implements HttpHandler {

        @Override

        public void handle(HttpExchange exchange) throws IOException {

            String response = "¡Hola, mundo!";

            exchange.sendResponseHeaders(200, response.length());

            OutputStream os = exchange.getResponseBody();

            os.write(response.getBytes());

            os.close();

        }

    }

}


La API de Servidor Web Simple en Java es una herramienta útil para desarrollar servidores HTTP de forma rápida y sencilla. Aunque tiene limitaciones en cuanto a escalabilidad y características avanzadas, es perfecta para pruebas y aplicaciones pequeñas sin necesidad de frameworks externos.


miércoles, 26 de febrero de 2025

API de Acceso a Memoria Externa en Java


La API de Acceso a Memoria Externa (Foreign Function & Memory API) en Java es una funcionalidad introducida para interactuar de manera segura y eficiente con memoria fuera del heap de Java. Esta API ha evolucionado en versiones recientes y permite mejorar la interoperabilidad con código nativo sin necesidad de JNI (Java Native Interface).

Esta API proporciona una forma segura y estructurada de acceder a la memoria fuera del control del recolector de basura, lo que permite mejorar el rendimiento en aplicaciones que requieren interacción con bibliotecas nativas o manipulación intensiva de datos.

Veamos un ejemplo básico de cómo asignar y manipular memoria externa en Java utilizando la API:


import java.lang.foreign.*;

import java.lang.invoke.VarHandle;


public class ForeignMemoryExample {

    public static void main(String[] args) {

        try (MemorySegment segment = MemorySegment.allocateNative(100)) {

            VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());

            intHandle.set(segment, 0, 42);

            System.out.println("Valor almacenado: " + intHandle.get(segment, 0));

        }

    }

}


Casos de uso:

  •  Interoperabilidad con bibliotecas en C: Facilita la comunicación con código nativo sin JNI.
  •  Manipulación de grandes volúmenes de datos: Ideal para manejar grandes bloques de memoria sin afectar el GC.
  •  Optimización en aplicaciones de alto rendimiento: Como motores de bases de datos y procesamiento de señales.


La API de Acceso a Memoria Externa en Java representa un avance importante en la forma en que interactuamos con memoria nativa. Al ofrecer una interfaz más segura y eficiente, facilita el desarrollo de aplicaciones de alto rendimiento sin comprometer la seguridad y estabilidad del código Java.


lunes, 24 de febrero de 2025

Pattern Matching en Java


La coincidencia de patrones (Pattern Matching) en switch es una característica introducida en Java 17 como una mejora en la sintaxis del switch, permitiendo evaluar tipos de manera más sencilla y concisa. Esta funcionalidad facilita la escritura de código más limpio y seguro al eliminar la necesidad de casting manual.

Veamos un ejemplo, antes de java 17 haciamos:

 

static void procesar(Object obj) {

    switch (obj.getClass().getSimpleName()) {

        case "String":

            String s = (String) obj;

            System.out.println("Es una cadena: " + s.toUpperCase());

            break;

        case "Integer":

            Integer i = (Integer) obj;

            System.out.println("Es un número: " + (i * 2));

            break;

        default:

            System.out.println("Tipo no soportado");

    }

}


Y en Java 17: 


static void procesar(Object obj) {

    switch (obj) {

        case String s -> System.out.println("Es una cadena: " + s.toUpperCase());

        case Integer i -> System.out.println("Es un número: " + (i * 2));

        default -> System.out.println("Tipo no soportado");

    }

}


Esta disponible a partir de Java 17 en vista previa y consolidado en versiones posteriores. Algo a tener en cuanta es que solo se puede utilizar en switch con expresiones y no con estructuras más antiguas.

El pattern matching en switch es una mejora significativa que permite un código más limpio y expresivo. Reduce el uso de casts y mejora la legibilidad del código, haciendo que la programación en Java sea más moderna y eficiente.


sábado, 22 de febrero de 2025

Clases Selladas en Java


Las clases selladas en Java, introducidas en Java 15 como una característica en vista previa y estandarizadas en Java 17, permiten restringir qué clases pueden heredar de una clase base, mejorando la seguridad y el diseño del código.

Las clases selladas (sealed) establecen un conjunto específico de clases que pueden extender o implementar una clase base, evitando la herencia no controlada.


public sealed class Figura permits Circulo, Rectangulo {}


public final class Circulo extends Figura {}

public final class Rectangulo extends Figura {}


Aquí, Figura es una clase sellada, lo que significa que solo Circulo y Rectangulo pueden heredar de ella.

Las clases que extienden una clase sellada deben usar una de las siguientes opciones:

  • final: No permite más subclases.
  • sealed: Permite definir otro conjunto restringido de subclases.
  • non-sealed: Permite herencia sin restricciones.


Ejemplo con diferentes modificaciones:


public sealed class Figura permits Circulo, Poligono {}


public final class Circulo extends Figura {}

public sealed class Poligono extends Figura permits Triangulo {}

public non-sealed class Triangulo extends Poligono {}


Las clases selladas en Java proporcionan un control más preciso sobre la herencia, facilitando la definición de modelos de dominio más seguros y expresivos. Son especialmente útiles cuando se combinan con switch y pattern matching para estructurar mejor el código.