Skip to content

Top 100 Preguntas Completas de Java

Esta guía cubre desde fundamentos hasta temas avanzados. Las preguntas 1-50 están en las guías anteriores.


¿Por qué es importante? Java 8 fue la actualización más significativa. Introdujo programación funcional.

Novedades principales:

  • Lambdas y Streams
  • Optional
  • Interfaces funcionales
  • Métodos default en interfaces
  • Method references
  • Nueva API de fecha/hora
  • Nashorn JavaScript Engine

¿Qué es? API moderna para fechas/horas que reemplaza a Date y Calendar.

Ventajas: Inmutable, thread-safe, más intuitiva, soporte de zonas horarias.

import java.time.*;
// Clases inmutables y thread-safe
LocalDate fecha = LocalDate.now();
LocalDate cumple = LocalDate.of(1990, Month.MARCH, 15);
LocalTime hora = LocalTime.now();
LocalTime mediodia = LocalTime.of(12, 0);
LocalDateTime fechaHora = LocalDateTime.now();
ZonedDateTime conZona = ZonedDateTime.now(ZoneId.of("America/Lima"));
// Operaciones
LocalDate manana = fecha.plusDays(1);
LocalDate haceMes = fecha.minusMonths(1);
// Período y duración
Period periodo = Period.between(cumple, fecha);
Duration duracion = Duration.between(hora, mediodia);
// Formateo
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String formateado = fecha.format(formatter);
LocalDate parseado = LocalDate.parse("15/03/1990", formatter);

Principal novedad: Sistema de módulos (Project Jigsaw) para encapsular código.

Otras mejoras: Factory methods para colecciones, mejoras en Stream y Optional.

// Módulos (Project Jigsaw)
module mi.modulo {
requires java.sql;
exports com.mi.paquete;
}
// Factory methods para colecciones
List<String> lista = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> mapa = Map.of("uno", 1, "dos", 2);
// Stream improvements
Stream.ofNullable(posibleNull);
stream.takeWhile(n -> n < 5);
stream.dropWhile(n -> n < 5);
// Optional improvements
optional.ifPresentOrElse(
valor -> System.out.println(valor),
() -> System.out.println("Vacío")
);
optional.or(() -> Optional.of("default"));
optional.stream();
// Private methods en interfaces
interface MiInterface {
private void metodoPrivado() {}
}

Principal novedad: var para inferencia de tipos en variables locales.

Importante: Solo para variables locales con inicializador, NO para campos o parámetros.

// var - inferencia de tipos local
var lista = new ArrayList<String>(); // ArrayList<String>
var mapa = Map.of("a", 1); // Map<String, Integer>
// Solo para variables locales con inicializador
// NO para campos, parámetros o return types
// copyOf para colecciones inmutables
List<String> copia = List.copyOf(listaOriginal);

¿Por qué es importante? Primera versión LTS después de Java 8. Muy usada en producción.

Novedades: var en lambdas, métodos de String, HttpClient, ejecutar .java directamente.

// var en lambdas
BiFunction<String, String, String> concat =
(var a, var b) -> a + b;
// Nuevos métodos de String
" hola ".strip(); // "hola" (mejor que trim)
" hola ".stripLeading(); // "hola "
" hola ".stripTrailing();// " hola"
"".isBlank(); // true
"hola
mundo".lines(); // Stream<String>
"ab".repeat(3); // "ababab"
// HTTP Client (estándar)
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com"))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString()
);
// Files
String contenido = Files.readString(Path.of("archivo.txt"));
Files.writeString(Path.of("archivo.txt"), "contenido");

Novedades clave: Records, Pattern Matching, Switch expressions, Text blocks, Sealed classes.

Java 17 es LTS - Versión recomendada para producción actualmente.

// Records (Java 14 preview, 16 final)
public record Persona(String nombre, int edad) {}
// Genera: constructor, getters, equals, hashCode, toString
// Pattern Matching para instanceof (Java 14 preview, 16 final)
if (obj instanceof String s) {
System.out.println(s.length());
}
// Switch expressions (Java 14)
String resultado = switch (dia) {
case LUNES, MARTES -> "Inicio de semana";
case MIERCOLES -> "Mitad de semana";
case JUEVES, VIERNES -> "Fin de semana laboral";
default -> "Fin de semana";
};
// Text blocks (Java 15)
String json = """
{
"nombre": "Juan",
"edad": 25
}
""";
// Sealed classes (Java 17)
public sealed class Figura permits Circulo, Rectangulo, Triangulo {}
public final class Circulo extends Figura {}
public non-sealed class Rectangulo extends Figura {}

¿Por qué es importante? Última versión LTS. Incluye Virtual Threads (revolucionario para concurrencia).

Novedades: Virtual Threads, Pattern Matching mejorado, Record Patterns, Sequenced Collections.

// Virtual Threads (Project Loom)
Thread.startVirtualThread(() -> {
System.out.println("Virtual thread");
});
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> hacerTarea());
}
// Pattern Matching para switch
String resultado = switch (obj) {
case Integer i -> "Entero: " + i;
case String s -> "String: " + s;
case null -> "Nulo";
default -> "Otro";
};
// Record Patterns
record Point(int x, int y) {}
if (obj instanceof Point(int x, int y)) {
System.out.println(x + ", " + y);
}
// Sequenced Collections
SequencedCollection<String> seq = new LinkedHashSet<>();
seq.addFirst("primero");
seq.addLast("último");
seq.getFirst();
seq.getLast();
seq.reversed();

¿Qué es? Threads ligeros gestionados por la JVM (no el OS). Puedes crear millones.

¿Para qué sirve? Operaciones I/O-bound (HTTP, BD). NO para CPU-intensive.

// Virtual Threads - threads ligeros (Project Loom)
// Pueden crear millones sin problema
// Crear virtual thread
Thread vThread = Thread.ofVirtual().start(() -> {
System.out.println("Virtual thread");
});
// Con ExecutorService
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
// Diferencias con Platform Threads:
// - Muy ligeros (KB vs MB)
// - Gestionados por JVM, no por OS
// - Ideales para I/O blocking
// - No usar para CPU-intensive

¿Qué es? Clase inmutable para transportar datos. Genera automáticamente constructor, getters, equals, hashCode, toString.

¿Para qué sirve? DTOs, value objects, eliminar boilerplate.

// Record - clase inmutable para datos
public record Persona(String nombre, int edad) {
// Constructor compacto para validación
public Persona {
if (edad < 0) {
throw new IllegalArgumentException("Edad negativa");
}
nombre = nombre.trim();
}
// Métodos adicionales
public String nombreMayusculas() {
return nombre.toUpperCase();
}
// Constructor adicional
public Persona(String nombre) {
this(nombre, 0);
}
}
// Uso
Persona p = new Persona("Juan", 25);
String nombre = p.nombre(); // getter
int edad = p.edad();
// Automáticamente tiene:
// - Constructor con todos los campos
// - Getters (nombre(), edad())
// - equals() y hashCode()
// - toString()

¿Qué es? Clases que restringen explícitamente qué otras clases pueden heredar de ellas.

¿Para qué sirve? Modelar dominios cerrados, mejor pattern matching, control de jerarquía.

// Sealed class - restringe qué clases pueden heredar
public sealed class Figura
permits Circulo, Rectangulo, Triangulo {
}
// Subclases deben ser: final, sealed, o non-sealed
public final class Circulo extends Figura {
private double radio;
}
public sealed class Rectangulo extends Figura
permits Cuadrado {
}
public final class Cuadrado extends Rectangulo {}
public non-sealed class Triangulo extends Figura {
// Cualquiera puede heredar de Triangulo
}
// Útil para pattern matching exhaustivo
double area = switch (figura) {
case Circulo c -> Math.PI * c.getRadio() * c.getRadio();
case Rectangulo r -> r.getAncho() * r.getAlto();
case Triangulo t -> t.getBase() * t.getAltura() / 2;
// No necesita default - es exhaustivo
};

¿Qué es? Extraer datos de objetos de forma declarativa, combinando instanceof + cast + asignación.

Tipos: Para instanceof, para switch, para records (deconstrucción).

// Pattern Matching para instanceof
Object obj = "Hola";
// Antes
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// Ahora
if (obj instanceof String s) {
System.out.println(s.length());
}
// Pattern Matching en switch
String resultado = switch (obj) {
case Integer i when i > 0 -> "Positivo: " + i;
case Integer i -> "No positivo: " + i;
case String s when s.length() > 5 -> "String largo";
case String s -> "String corto: " + s;
case null -> "Nulo";
default -> "Otro tipo";
};
// Record Patterns
record Point(int x, int y) {}
record Line(Point start, Point end) {}
void procesar(Object obj) {
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
System.out.println("Línea de (" + x1 + "," + y1 + ") a (" + x2 + "," + y2 + ")");
}
}

¿Qué es? Strings multilínea con triple comilla. Preservan formato e indentación.

¿Para qué sirve? JSON, HTML, SQL, cualquier texto largo.

// Text Blocks - strings multilínea
String json = """
{
"nombre": "Juan",
"edad": 25,
"activo": true
}
""";
String html = """
<html>
<body>
<h1>Título</h1>
</body>
</html>
""";
// Caracteres de escape
String conComillas = """
Él dijo: "Hola"
""";
// Interpolación (no nativa, usar formatted)
String nombre = "Juan";
String saludo = """
Hola, %s!
Bienvenido.
""".formatted(nombre);

¿Qué es? Switch que retorna un valor. Usa -> en vez de : y no necesita break.

Ventajas: Más conciso, sin fall-through accidental, exhaustivo.

// Switch tradicional (statement)
String tipo;
switch (dia) {
case LUNES:
case MARTES:
tipo = "Inicio";
break;
default:
tipo = "Otro";
}
// Switch expression (Java 14+)
String tipo = switch (dia) {
case LUNES, MARTES -> "Inicio";
case MIERCOLES -> "Mitad";
case JUEVES, VIERNES -> "Fin laboral";
case SABADO, DOMINGO -> "Fin de semana";
};
// Con yield para bloques
String descripcion = switch (dia) {
case LUNES -> {
System.out.println("Procesando lunes");
yield "Primer día";
}
default -> "Otro día";
};

¿Qué es? GC por defecto desde Java 9. Divide heap en regiones, prioriza las más llenas.

Características: Pausas predecibles, ideal para heaps >4GB.

// G1 GC - Garbage First (default desde Java 9)
// Divide el heap en regiones de igual tamaño
// Características:
// - Pausas predecibles
// - Compactación concurrente
// - Ideal para heaps grandes (>4GB)
// Configuración
// -XX:+UseG1GC (default)
// -XX:MaxGCPauseMillis=200
// -XX:G1HeapRegionSize=16m
// Otros GCs disponibles:
// - ZGC: Ultra-low latency (<1ms pausas)
// -XX:+UseZGC
// - Shenandoah: Low-pause
// -XX:+UseShenandoahGC
// - Parallel GC: Throughput
// -XX:+UseParallelGC

¿Qué es? Compilador que convierte bytecode a código nativo en runtime para mejorar performance.

Tipos: C1 (rápido, menos optimizado), C2 (lento, más optimizado), Tiered (ambos, default).

  • C1 (Client): Compilación rápida, menos optimizaciones
  • C2 (Server): Compilación lenta, más optimizaciones
  • Tiered Compilation: Usa ambos (default)
// Ver compilación JIT
// -XX:+PrintCompilation
// GraalVM - JIT alternativo más avanzado
// Ahead-of-Time compilation con native-image

¿Qué es? Patrón que delega la creación de objetos a un método/clase especializada.

¿Para qué sirve? Desacoplar creación de uso, crear objetos según condiciones.

// Factory Method
public interface Animal {
void hablar();
}
public class Perro implements Animal {
public void hablar() { System.out.println("Guau"); }
}
public class Gato implements Animal {
public void hablar() { System.out.println("Miau"); }
}
public class AnimalFactory {
public static Animal crear(String tipo) {
return switch (tipo.toLowerCase()) {
case "perro" -> new Perro();
case "gato" -> new Gato();
default -> throw new IllegalArgumentException("Tipo desconocido");
};
}
}
// Uso
Animal animal = AnimalFactory.crear("perro");

¿Qué es? Patrón para construir objetos complejos paso a paso.

¿Para qué sirve? Objetos con muchos parámetros opcionales, inmutabilidad, legibilidad.

public class Persona {
private final String nombre;
private final int edad;
private final String email;
private final String telefono;
private Persona(Builder builder) {
this.nombre = builder.nombre;
this.edad = builder.edad;
this.email = builder.email;
this.telefono = builder.telefono;
}
public static class Builder {
private String nombre;
private int edad;
private String email;
private String telefono;
public Builder nombre(String nombre) {
this.nombre = nombre;
return this;
}
public Builder edad(int edad) {
this.edad = edad;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder telefono(String telefono) {
this.telefono = telefono;
return this;
}
public Persona build() {
return new Persona(this);
}
}
}
// Uso
Persona persona = new Persona.Builder()
.nombre("Juan")
.edad(25)
.email("juan@email.com")
.build();

¿Qué es? Patrón donde un objeto (subject) notifica cambios a múltiples objetos dependientes (observers).

¿Para qué sirve? Eventos, notificaciones, actualizaciones en tiempo real.

import java.util.*;
// Subject
public class EventoNotificador {
private List<Observador> observadores = new ArrayList<>();
public void agregar(Observador o) {
observadores.add(o);
}
public void remover(Observador o) {
observadores.remove(o);
}
public void notificar(String evento) {
for (Observador o : observadores) {
o.actualizar(evento);
}
}
}
// Observer
public interface Observador {
void actualizar(String evento);
}
public class EmailObservador implements Observador {
@Override
public void actualizar(String evento) {
System.out.println("Email: " + evento);
}
}
// Uso
EventoNotificador notificador = new EventoNotificador();
notificador.agregar(new EmailObservador());
notificador.notificar("Nuevo pedido");

¿Qué es? Patrón que define una familia de algoritmos intercambiables en runtime.

¿Para qué sirve? Métodos de pago, algoritmos de ordenamiento, validaciones.

// Strategy interface
public interface EstrategiaPago {
void pagar(double monto);
}
// Estrategias concretas
public class PagoTarjeta implements EstrategiaPago {
@Override
public void pagar(double monto) {
System.out.println("Pagando $" + monto + " con tarjeta");
}
}
public class PagoPayPal implements EstrategiaPago {
@Override
public void pagar(double monto) {
System.out.println("Pagando $" + monto + " con PayPal");
}
}
// Context
public class CarritoCompras {
private EstrategiaPago estrategia;
public void setEstrategia(EstrategiaPago estrategia) {
this.estrategia = estrategia;
}
public void checkout(double monto) {
estrategia.pagar(monto);
}
}
// Uso
CarritoCompras carrito = new CarritoCompras();
carrito.setEstrategia(new PagoTarjeta());
carrito.checkout(100.0);

¿Qué es? Patrón que añade funcionalidad a objetos dinámicamente sin modificar su clase.

¿Para qué sirve? Streams de Java, wrappers, extensiones de funcionalidad.

// Component
public interface Cafe {
double getCosto();
String getDescripcion();
}
// Concrete Component
public class CafeSimple implements Cafe {
public double getCosto() { return 1.0; }
public String getDescripcion() { return "Café"; }
}
// Decorator
public abstract class CafeDecorator implements Cafe {
protected Cafe cafe;
public CafeDecorator(Cafe cafe) {
this.cafe = cafe;
}
}
// Concrete Decorators
public class ConLeche extends CafeDecorator {
public ConLeche(Cafe cafe) { super(cafe); }
public double getCosto() { return cafe.getCosto() + 0.5; }
public String getDescripcion() { return cafe.getDescripcion() + " + Leche"; }
}
public class ConAzucar extends CafeDecorator {
public ConAzucar(Cafe cafe) { super(cafe); }
public double getCosto() { return cafe.getCosto() + 0.2; }
public String getDescripcion() { return cafe.getDescripcion() + " + Azúcar"; }
}
// Uso
Cafe cafe = new ConAzucar(new ConLeche(new CafeSimple()));
System.out.println(cafe.getDescripcion()); // Café + Leche + Azúcar
System.out.println(cafe.getCosto()); // 1.7

¿Qué es? Patrón que permite que interfaces incompatibles trabajen juntas.

¿Para qué sirve? Integrar librerías externas, legacy code, APIs diferentes.

// Target interface
public interface MediaPlayer {
void play(String filename);
}
// Adaptee (clase existente con interfaz incompatible)
public class VlcPlayer {
public void playVlc(String filename) {
System.out.println("Playing VLC: " + filename);
}
}
// Adapter
public class VlcAdapter implements MediaPlayer {
private VlcPlayer vlcPlayer;
public VlcAdapter() {
this.vlcPlayer = new VlcPlayer();
}
@Override
public void play(String filename) {
vlcPlayer.playVlc(filename);
}
}
// Uso
MediaPlayer player = new VlcAdapter();
player.play("video.vlc");

¿Qué es? Patrón que proporciona un sustituto o placeholder para controlar acceso a otro objeto.

Tipos: Virtual (lazy loading), Protection (control de acceso), Remote (objetos remotos).

// Subject
public interface Imagen {
void mostrar();
}
// Real Subject
public class ImagenReal implements Imagen {
private String archivo;
public ImagenReal(String archivo) {
this.archivo = archivo;
cargarDesdeDisc();
}
private void cargarDesdeDisc() {
System.out.println("Cargando " + archivo);
}
@Override
public void mostrar() {
System.out.println("Mostrando " + archivo);
}
}
// Proxy
public class ImagenProxy implements Imagen {
private ImagenReal imagenReal;
private String archivo;
public ImagenProxy(String archivo) {
this.archivo = archivo;
}
@Override
public void mostrar() {
if (imagenReal == null) {
imagenReal = new ImagenReal(archivo); // Lazy loading
}
imagenReal.mostrar();
}
}

¿Qué es? Patrón que define el esqueleto de un algoritmo, delegando pasos a subclases.

¿Para qué sirve? Reutilizar estructura común, permitir personalización de pasos específicos.

// Abstract class con template method
public abstract class Bebida {
// Template method
public final void preparar() {
hervirAgua();
agregarIngrediente();
servir();
if (clienteQuiereCondimentos()) {
agregarCondimentos();
}
}
private void hervirAgua() {
System.out.println("Hirviendo agua");
}
protected abstract void agregarIngrediente();
private void servir() {
System.out.println("Sirviendo en taza");
}
protected abstract void agregarCondimentos();
// Hook method
protected boolean clienteQuiereCondimentos() {
return true;
}
}
public class Cafe extends Bebida {
@Override
protected void agregarIngrediente() {
System.out.println("Agregando café");
}
@Override
protected void agregarCondimentos() {
System.out.println("Agregando azúcar y leche");
}
}

¿Qué es? Patrón que encapsula una solicitud como objeto.

¿Para qué sirve? Undo/redo, colas de comandos, logging, transacciones.

// Command interface
public interface Comando {
void ejecutar();
void deshacer();
}
// Receiver
public class Luz {
public void encender() { System.out.println("Luz encendida"); }
public void apagar() { System.out.println("Luz apagada"); }
}
// Concrete Command
public class EncenderLuzComando implements Comando {
private Luz luz;
public EncenderLuzComando(Luz luz) {
this.luz = luz;
}
@Override
public void ejecutar() { luz.encender(); }
@Override
public void deshacer() { luz.apagar(); }
}
// Invoker
public class Control {
private Comando comando;
public void setComando(Comando comando) {
this.comando = comando;
}
public void presionarBoton() {
comando.ejecutar();
}
}

¿Qué es? Patrón que permite a un objeto cambiar su comportamiento cuando cambia su estado interno.

¿Para qué sirve? Máquinas de estado, workflows, evitar condicionales complejos.

// State interface
public interface Estado {
void insertar();
void expulsar();
void dispensar();
}
// Context
public class MaquinaExpendedora {
private Estado estado;
public void setEstado(Estado estado) {
this.estado = estado;
}
public void insertar() { estado.insertar(); }
public void expulsar() { estado.expulsar(); }
public void dispensar() { estado.dispensar(); }
}
// Concrete States
public class SinMoneda implements Estado {
private MaquinaExpendedora maquina;
@Override
public void insertar() {
System.out.println("Moneda insertada");
maquina.setEstado(new ConMoneda(maquina));
}
@Override
public void expulsar() {
System.out.println("No hay moneda");
}
@Override
public void dispensar() {
System.out.println("Inserte moneda primero");
}
}

¿Qué es? Patrón donde las dependencias se proporcionan externamente en vez de crearlas internamente.

Beneficios: Desacoplamiento, testabilidad, flexibilidad.

// Sin DI (acoplado)
public class ServicioUsuario {
private RepositorioUsuario repo = new RepositorioUsuarioImpl();
}
// Con DI (desacoplado)
public class ServicioUsuario {
private final RepositorioUsuario repo;
// Constructor injection (preferido)
public ServicioUsuario(RepositorioUsuario repo) {
this.repo = repo;
}
}
// Setter injection
public class ServicioUsuario {
private RepositorioUsuario repo;
public void setRepo(RepositorioUsuario repo) {
this.repo = repo;
}
}
// Uso
RepositorioUsuario repo = new RepositorioUsuarioImpl();
ServicioUsuario servicio = new ServicioUsuario(repo);

¿Qué es? Cinco principios de diseño orientado a objetos para código mantenible y escalable.

PrincipioDescripción
Single ResponsibilityUna clase, una responsabilidad
Open/ClosedAbierto para extensión, cerrado para modificación
Liskov SubstitutionSubtipos deben ser sustituibles por sus tipos base
Interface SegregationInterfaces específicas mejor que una general
Dependency InversionDepender de abstracciones, no de implementaciones

¿Qué es? Patrón que abstrae el acceso a datos, proporcionando una colección de objetos de dominio.

Beneficios: Desacopla lógica de negocio de persistencia, facilita testing.

// Repository interface
public interface UsuarioRepository {
Optional<Usuario> findById(Long id);
List<Usuario> findAll();
Usuario save(Usuario usuario);
void delete(Long id);
List<Usuario> findByNombre(String nombre);
}
// Implementation
public class UsuarioRepositoryImpl implements UsuarioRepository {
private EntityManager em;
@Override
public Optional<Usuario> findById(Long id) {
return Optional.ofNullable(em.find(Usuario.class, id));
}
@Override
public List<Usuario> findAll() {
return em.createQuery("SELECT u FROM Usuario u", Usuario.class)
.getResultList();
}
@Override
public Usuario save(Usuario usuario) {
if (usuario.getId() == null) {
em.persist(usuario);
} else {
usuario = em.merge(usuario);
}
return usuario;
}
}

¿Qué es? Capa que contiene la lógica de negocio, orquestando operaciones entre repositorios.

Responsabilidades: Validaciones, transacciones, coordinación de operaciones.

// Service layer - lógica de negocio
@Service
public class UsuarioService {
private final UsuarioRepository repository;
private final EmailService emailService;
public UsuarioService(UsuarioRepository repository, EmailService emailService) {
this.repository = repository;
this.emailService = emailService;
}
@Transactional
public Usuario registrar(UsuarioDTO dto) {
// Validaciones de negocio
if (repository.existsByEmail(dto.getEmail())) {
throw new EmailDuplicadoException();
}
// Crear usuario
Usuario usuario = new Usuario();
usuario.setNombre(dto.getNombre());
usuario.setEmail(dto.getEmail());
usuario.setPassword(encriptar(dto.getPassword()));
// Guardar
usuario = repository.save(usuario);
// Enviar email de bienvenida
emailService.enviarBienvenida(usuario);
return usuario;
}
}

¿Qué es? Objeto para transferir datos entre capas/sistemas sin exponer entidades de dominio.

¿Para qué sirve? Seguridad (ocultar campos), performance (solo datos necesarios).

// DTO - Data Transfer Object
public class UsuarioDTO {
private String nombre;
private String email;
// Sin password ni datos sensibles
// Getters y setters
}
// Record como DTO (Java 16+)
public record UsuarioDTO(String nombre, String email) {}
// Mapper
public class UsuarioMapper {
public static UsuarioDTO toDTO(Usuario usuario) {
return new UsuarioDTO(
usuario.getNombre(),
usuario.getEmail()
);
}
public static Usuario toEntity(UsuarioDTO dto) {
Usuario usuario = new Usuario();
usuario.setNombre(dto.nombre());
usuario.setEmail(dto.email());
return usuario;
}
}

¿Qué es? Framework de testing para Java. Versión actual con arquitectura modular.

Componentes: JUnit Platform, JUnit Jupiter (API), JUnit Vintage (compatibilidad).

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CalculadoraTest {
private Calculadora calc;
@BeforeEach
void setUp() {
calc = new Calculadora();
}
@Test
@DisplayName("Suma de dos números positivos")
void testSumar() {
assertEquals(5, calc.sumar(2, 3));
}
@Test
void testDividirPorCero() {
assertThrows(ArithmeticException.class, () -> calc.dividir(1, 0));
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testEsPositivo(int numero) {
assertTrue(calc.esPositivo(numero));
}
@RepeatedTest(5)
void testRepetido() {
assertNotNull(calc);
}
}

¿Qué es? Framework para crear mocks (objetos simulados) en tests unitarios.

¿Para qué sirve? Aislar la unidad bajo test, simular dependencias.

import static org.mockito.Mockito.*;
class UsuarioServiceTest {
@Mock
private UsuarioRepository repository;
@InjectMocks
private UsuarioService service;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testBuscarUsuario() {
// Arrange
Usuario usuario = new Usuario(1L, "Juan");
when(repository.findById(1L)).thenReturn(Optional.of(usuario));
// Act
Usuario resultado = service.buscar(1L);
// Assert
assertEquals("Juan", resultado.getNombre());
verify(repository, times(1)).findById(1L);
}
@Test
void testGuardarUsuario() {
Usuario usuario = new Usuario(null, "Ana");
when(repository.save(any(Usuario.class))).thenReturn(new Usuario(1L, "Ana"));
Usuario guardado = service.guardar(usuario);
assertNotNull(guardado.getId());
verify(repository).save(usuario);
}
}

¿Qué es? Test-Driven Development: escribir tests ANTES del código de producción.

Ciclo:

  1. Red: Escribir test que falla
  2. Green: Escribir código mínimo para pasar
  3. Refactor: Mejorar código manteniendo tests verdes

¿Qué es? Behavior-Driven Development: tests escritos en lenguaje natural (Given-When-Then).

Herramientas: Cucumber, JBehave.

// Con Cucumber
Feature: Login de usuario
Scenario: Login exitoso
Given un usuario registrado con email "juan@test.com"
When ingresa su password correcto
Then debe ver el dashboard
// Step definitions
@Given("un usuario registrado con email {string}")
public void usuarioRegistrado(String email) {
usuario = new Usuario(email);
}
@When("ingresa su password correcto")
public void ingresaPassword() {
resultado = loginService.login(usuario, "password");
}
@Then("debe ver el dashboard")
public void verDashboard() {
assertTrue(resultado.isExitoso());
}

¿Qué es? Porcentaje de código ejecutado por tests.

Herramientas: JaCoCo, Cobertura. Meta típica: 80%+ para código crítico.

// Maven con JaCoCo
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>

86. ¿Qué son las anotaciones de validación?

Section titled “86. ¿Qué son las anotaciones de validación?”

¿Qué es? Bean Validation (JSR 380): validar objetos con anotaciones.

Anotaciones comunes: @NotNull, @Size, @Email, @Min, @Max, @Pattern.

import jakarta.validation.constraints.*;
public class UsuarioDTO {
@NotNull(message = "Nombre requerido")
@Size(min = 2, max = 50)
private String nombre;
@Email(message = "Email inválido")
@NotBlank
private String email;
@Min(18)
@Max(120)
private int edad;
@Pattern(regexp = "^[0-9]{9}$")
private String telefono;
@Past
private LocalDate fechaNacimiento;
}

¿Qué es? Registrar eventos de la aplicación para debugging y monitoreo.

Stack recomendado: SLF4J (API) + Logback (implementación).

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MiServicio {
private static final Logger log = LoggerFactory.getLogger(MiServicio.class);
public void procesar() {
log.debug("Iniciando proceso");
log.info("Procesando datos");
log.warn("Advertencia: datos incompletos");
log.error("Error al procesar", exception);
}
}
// logback.xml
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

Maven: Basado en XML, convención sobre configuración, más usado.

Gradle: Basado en Groovy/Kotlin, más flexible y rápido.

MavenGradle
XML (pom.xml)Groovy/Kotlin (build.gradle)
Convención sobre configuraciónMás flexible
Más verbosoMás conciso
Más lentoMás rápido (incremental)
// Maven pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
// Gradle build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
}

¿Qué es? Contenerizar aplicaciones Java para despliegue consistente.

Best practices: Multi-stage builds, usar JRE (no JDK), imágenes Alpine.

# Dockerfile
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/mi-app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# Multi-stage build
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:21-jre-alpine
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

90. ¿Cuáles son las mejores prácticas de Java?

Section titled “90. ¿Cuáles son las mejores prácticas de Java?”

Principios clave para código limpio y mantenible:

  1. Usar tipos inmutables cuando sea posible
  2. Preferir composición sobre herencia
  3. Programar contra interfaces
  4. Usar Optional en vez de null
  5. Cerrar recursos con try-with-resources
  6. Usar constantes en vez de magic numbers
  7. Seguir convenciones de nombres
  8. Documentar código público con Javadoc
  9. Escribir tests unitarios
  10. Usar logging en vez de System.out

91. ¿Qué es una arquitectura de microservicios?

Section titled “91. ¿Qué es una arquitectura de microservicios?”

¿Qué es? Aplicación dividida en servicios pequeños, independientes, desplegables por separado.

Comunicación: REST, gRPC, mensajería (Kafka, RabbitMQ).

¿Qué es? Punto de entrada único para microservicios.

Funciones: Routing, autenticación, rate limiting, load balancing, transformación.

¿Qué es? Patrón que previene cascada de fallos cortando llamadas a servicios fallidos.

Estados: Closed (normal), Open (bloqueado), Half-Open (probando).

¿Qué es? Almacenar cambios como secuencia de eventos en vez de estado actual.

Beneficios: Auditoría completa, reconstruir estado, debugging temporal.

¿Qué es? Command Query Responsibility Segregation: modelos separados para lectura y escritura.

¿Cuándo usarlo? Sistemas con patrones de lectura/escritura muy diferentes.

¿Qué es? Sistema de mensajería asíncrona que desacopla productores y consumidores.

Ejemplos: Kafka (streaming), RabbitMQ (colas tradicionales).

¿Qué es? Bases de datos no relacionales, optimizadas para casos de uso específicos.

Tipos: Document (MongoDB), Key-Value (Redis), Column (Cassandra), Graph (Neo4j).

¿Qué es? Almacenar datos frecuentes en memoria rápida para reducir latencia.

Herramientas: Redis (distribuido), Caffeine (local), EhCache.

¿Qué es? Distribuye tráfico entre múltiples instancias de un servicio.

Algoritmos: Round Robin, Least Connections, IP Hash, Weighted.

¿Qué es? Capacidad de entender el estado interno del sistema.

Pilares: Logs, Métricas, Traces. Herramientas: OpenTelemetry, Prometheus, Grafana, Jaeger.


🐝