Top 50 Preguntas Frecuentes de Java
Las 50 Preguntas Más Frecuentes de Java
Section titled “Las 50 Preguntas Más Frecuentes de Java”Guía completa con las preguntas que aparecen en la mayoría de entrevistas técnicas de nivel intermedio-avanzado.
1. ¿Qué es la JVM, JRE y JDK?
Section titled “1. ¿Qué es la JVM, JRE y JDK?”Definición profesional: Tres componentes del ecosistema Java con responsabilidades distintas: JVM ejecuta bytecode, JRE añade las bibliotecas estándar, JDK añade las herramientas de desarrollo.
| Componente | Descripción | ¿Para quién? |
|---|---|---|
| JVM | Java Virtual Machine - Ejecuta bytecode | Interno |
| JRE | JVM + bibliotecas estándar | Usuarios finales |
| JDK | JRE + compilador javac + herramientas | Desarrolladores |
Relación: JDK ⊇ JRE ⊇ JVM.
Cómo responder en entrevista:
“JVM ejecuta el bytecode en cualquier plataforma. JRE incluye la JVM más las librerías estándar para ejecutar apps. JDK incluye el JRE más el compilador javac y herramientas de desarrollo. La relación es JDK ⊇ JRE ⊇ JVM.”
2. ¿Qué es bytecode?
Section titled “2. ¿Qué es bytecode?”Definición profesional:
Código intermedio (archivos .class) generado por javac. La JVM lo interpreta y el JIT lo compila a código nativo en tiempo de ejecución para optimizar partes frecuentes.
// Código.java → javac → Código.class (bytecode) → JVM → JIT → Código nativo// Permite Write Once, Run Anywhere (WORA)// javap -c MiClase.class ← ver el bytecodeCómo responder en entrevista:
“El bytecode es independiente de la plataforma. La JVM lo interpreta y el JIT compiler optimiza las partes más ejecutadas compilándolas a código nativo del CPU. Esto logra el balance entre portabilidad y rendimiento.”
3. ¿Cuáles son los tipos de datos primitivos?
Section titled “3. ¿Cuáles son los tipos de datos primitivos?”¿Qué es? Java tiene 8 tipos de datos primitivos (no son objetos).
Tipos enteros:
- byte: 8 bits (-128 a 127)
- short: 16 bits
- int: 32 bits (más usado)
- long: 64 bits (sufijo
L)
Tipos decimales:
- float: 32 bits (sufijo
f) - double: 64 bits (más usado)
Otros:
- char: 16 bits (Unicode)
- boolean: true/false
Ejemplo práctico:
// 8 tipos primitivosbyte b = 127; // 8 bits, -128 a 127short s = 32767; // 16 bitsint i = 2147483647; // 32 bitslong l = 9223372036854775807L; // 64 bits
float f = 3.14f; // 32 bitsdouble d = 3.14159265; // 64 bits
char c = 'A'; // 16 bits (Unicode)boolean bool = true; // true/falseCómo responder en entrevista:
“Java tiene 8 primitivos: byte, short, int y long para enteros; float y double para decimales; char para Unicode y boolean. Se almacenan en el stack directamente, no son objetos. Para usarlos en colecciones existen sus Wrappers: Integer, Double, etc., que se convierten automáticamente con autoboxing.”
4. ¿Qué es autoboxing y unboxing?
Section titled “4. ¿Qué es autoboxing y unboxing?”¿Qué es? Conversión automática entre tipos primitivos y sus clases Wrapper.
Definiciones:
- Autoboxing: primitivo → Wrapper (
int→Integer) - Unboxing: Wrapper → primitivo (
Integer→int)
¿Para qué sirve?
- Usar primitivos en colecciones (
List<Integer>) - Simplificar código (conversión automática)
Cuidado: Unboxing de null lanza NullPointerException.
Ejemplo práctico:
// Autoboxing: primitivo → Wrapperint primitivo = 10;Integer wrapper = primitivo; // Autoboxing
// Unboxing: Wrapper → primitivoInteger wrapper2 = Integer.valueOf(20);int primitivo2 = wrapper2; // Unboxing
// Cuidado con nullInteger nulo = null;int valor = nulo; // NullPointerException!Cómo responder en entrevista:
“Autoboxing convierte int a Integer automáticamente; unboxing hace la inversa. El compilador lo inserta. El peligro es hacer unboxing de null, que lanza NullPointerException en runtime. También hay un impacto de rendimiento al usar Wrappers en bucles intensivos.”
5. ¿Qué es el operador ternario?
Section titled “5. ¿Qué es el operador ternario?”Definición profesional:
Operador condicional condicion ? valorSiTrue : valorSiFalse que reemplaza if-else de asignación en una sola expresión.
int edad = 20;String mensaje = (edad >= 18) ? "Mayor de edad" : "Menor de edad";
// Equivalente a:String mensaje2;if (edad >= 18) { mensaje2 = "Mayor de edad";} else { mensaje2 = "Menor de edad";}Cómo responder en entrevista:
“El operador ternario es azúcar sintáctica para un if-else de asignación. Lo uso cuando el código queda más legible en una línea. Para lógica compleja prefiero if-else tradicional para mantener la legibilidad y facilitar el debugging.”
6. ¿Qué es un constructor?
Section titled “6. ¿Qué es un constructor?”¿Qué es?
Método especial que se ejecuta al crear un objeto con new.
Características:
- Mismo nombre que la clase
- Sin tipo de retorno (ni siquiera
void) - Puede sobrecargarse (múltiples constructores)
- Si no defines ninguno, Java crea uno por defecto sin parámetros
Tipos comunes:
- Por defecto: Sin parámetros
- Parametrizado: Con parámetros para inicializar
- Copia: Recibe otro objeto del mismo tipo
Ejemplo práctico:
public class Persona { private String nombre; private int edad;
// Constructor por defecto public Persona() { this.nombre = "Sin nombre"; this.edad = 0; }
// Constructor con parámetros public Persona(String nombre, int edad) { this.nombre = nombre; this.edad = edad; }
// Constructor copia public Persona(Persona otra) { this.nombre = otra.nombre; this.edad = otra.edad; }}
// UsoPersona p1 = new Persona();Persona p2 = new Persona("Juan", 25);Persona p3 = new Persona(p2);Cómo responder en entrevista:
“Un constructor inicializa el objeto al crearlo. Tiene el mismo nombre de la clase y sin tipo de retorno. Se pueden sobrecargar para distintas formas de creación. Si no defines ninguno, Java provee uno por defecto sin parámetros. Con
this()se puede delegar a otro constructor de la misma clase.”
7. ¿Qué es this y super?
Section titled “7. ¿Qué es this y super?”¿Qué es? Palabras clave para referenciar al objeto actual y a la clase padre.
this:
- Referencia al objeto actual
- Distinguir entre parámetros y atributos con mismo nombre
- Llamar a otro constructor de la misma clase:
this()
super:
- Referencia a la clase padre
- Acceder a miembros del padre ocultos por el hijo
- Llamar al constructor del padre:
super()
Ejemplo práctico:
public class Animal { protected String nombre;
public Animal(String nombre) { this.nombre = nombre; // this = referencia al objeto actual }}
public class Perro extends Animal { private String raza;
public Perro(String nombre, String raza) { super(nombre); // super = referencia a la clase padre this.raza = raza; }
public void mostrar() { System.out.println(super.nombre); // Acceder a miembro del padre }}Cómo responder en entrevista:
“
thisreferencia al objeto actual y se usa para distinguir campos de parámetros o llamar a otro constructor conthis().superreferencia a la clase padre y se usa para llamar a su constructor consuper()o acceder a sus métodos sobrescritos.super()debe ser siempre la primera línea del constructor hijo.”
8. ¿Qué es sobrecarga (overloading) vs sobrescritura (overriding)?
Section titled “8. ¿Qué es sobrecarga (overloading) vs sobrescritura (overriding)?”¿Qué es? Dos formas de polimorfismo en Java.
Diferencias:
| Sobrecarga (Overloading) | Sobrescritura (Overriding) | |
|---|---|---|
| Clase | Misma clase | Clase hija |
| Método | Mismo nombre, diferentes parámetros | Misma firma exacta |
| Resolución | Compile-time | Runtime |
| Anotación | No necesaria | @Override recomendada |
Ejemplo práctico:
// SOBRECARGA - mismo nombre, diferentes parámetros (compile-time)public class Calculadora { public int sumar(int a, int b) { return a + b; }
public double sumar(double a, double b) { return a + b; }
public int sumar(int a, int b, int c) { return a + b + c; }}
// SOBRESCRITURA - redefinir método del padre (runtime)public class Animal { public void hacerSonido() { System.out.println("Sonido genérico"); }}
public class Perro extends Animal { @Override public void hacerSonido() { System.out.println("Guau!"); }}Cómo responder en entrevista:
“La sobrecarga ocurre en la misma clase con el mismo nombre de método pero distintos parámetros, se resuelve en compilación. La sobrescritura redefine un método heredado con exactamente la misma firma, se resuelve en runtime mediante polimorfismo.
@Overridees opcional pero recomendada para detectar errores de compilación.”
9. ¿Qué es static?
Section titled “9. ¿Qué es static?”¿Qué es? Modificador que indica que un miembro pertenece a la CLASE, no a instancias individuales.
Usos:
- Variables estáticas: Compartidas entre todas las instancias
- Métodos estáticos: Se llaman sin crear objeto (
Math.sqrt()) - Bloques estáticos: Se ejecutan una vez al cargar la clase
- Clases internas estáticas: No necesitan instancia de la clase externa
Ejemplo práctico:
public class Contador { // Variable estática - compartida entre todas las instancias private static int totalInstancias = 0;
// Variable de instancia - única por objeto private int id;
public Contador() { totalInstancias++; this.id = totalInstancias; }
// Método estático - se llama sin crear instancia public static int getTotalInstancias() { return totalInstancias; }
// Bloque estático - se ejecuta al cargar la clase static { System.out.println("Clase cargada"); }}
// UsoContador c1 = new Contador();Contador c2 = new Contador();System.out.println(Contador.getTotalInstancias()); // 2Cómo responder en entrevista:
“
staticindica que el miembro pertenece a la clase, no a una instancia. Las variables static son compartidas por todos los objetos. Los métodos static se invocan directamente en la clase sin crear objeto, comoMath.sqrt(). Los bloques static se ejecutan una sola vez al cargar la clase.”
10. ¿Qué es volatile?
Section titled “10. ¿Qué es volatile?”¿Qué es? Modificador que garantiza visibilidad de cambios entre threads.
¿Para qué sirve?
- Los cambios son visibles INMEDIATAMENTE para todos los threads
- Evita que los threads cacheen el valor localmente
- Útil para flags de control (ej:
boolean running)
Limitación: NO garantiza atomicidad. Para operaciones atómicas, usar AtomicInteger, etc.
Ejemplo práctico:
public class Ejemplo { // volatile garantiza visibilidad entre threads private volatile boolean activo = true;
public void detener() { activo = false; // Visible inmediatamente para otros threads }
public void ejecutar() { while (activo) { // Hacer algo } }}Cómo responder en entrevista:
“
volatilegarantiza que un campo se lea/escriba en memoria principal, no en caché del CPU, así todos los threads ven el valor más reciente. No garantiza atomicidad: uncontador++sigue siendo una operación compuesta no atómica. Para incrementos atómicos usoAtomicInteger; para bloques de código usosynchronized.”
11. ¿Qué es transient?
Section titled “11. ¿Qué es transient?”Definición profesional: Modificador que excluye un campo del proceso de serialización. Al deserializar, el campo toma su valor por defecto.
Ejemplo práctico:
public class Usuario implements Serializable { private String nombre; private transient String password; // No se serializa
// Al deserializar, password será null}Cómo responder en entrevista:
“
transientexcluye un campo de la serialización. Lo uso para datos sensibles como contraseñas o tokens, y para referencias a objetos no serializables como conexiones a BD. Al deserializar el campo valenullpara objetos o0/falsepara primitivos.”
12. ¿Qué es synchronized?
Section titled “12. ¿Qué es synchronized?”¿Qué es? Mecanismo para garantizar que solo UN thread ejecute un bloque de código a la vez.
¿Para qué sirve?
- Prevenir condiciones de carrera (race conditions)
- Proteger recursos compartidos
- Garantizar consistencia de datos
Formas de uso:
- Método sincronizado: Todo el método es exclusivo
- Bloque sincronizado: Solo una parte del código (más granular)
Ejemplo práctico:
public class Contador { private int valor = 0;
// Método sincronizado public synchronized void incrementar() { valor++; }
// Bloque sincronizado public void decrementar() { synchronized(this) { valor--; } }}Cómo responder en entrevista:
“
synchronizedgarantiza que solo un thread ejecute el bloque a la vez usando el monitor del objeto. En métodos de instancia el monitor esthis; en métodosstatices la clase. El bloque sincronizado es más granular y eficiente. Para alta concurrencia prefieroReentrantLocko las estructurasconcurrentdel paquetejava.util.concurrent.”
13. ¿Qué es un Singleton?
Section titled “13. ¿Qué es un Singleton?”¿Qué es? Patrón de diseño que garantiza UNA SOLA instancia de una clase en toda la aplicación.
¿Para qué sirve?
- Conexiones a base de datos
- Configuración de la aplicación
- Loggers
- Cachés
Implementación:
- Constructor privado
- Instancia estática privada
- Método público estático para obtenerla
Ejemplo práctico:
// Singleton - una sola instanciapublic class Singleton { private static volatile Singleton instancia;
private Singleton() {} // Constructor privado
public static Singleton getInstancia() { if (instancia == null) { synchronized (Singleton.class) { if (instancia == null) { instancia = new Singleton(); } } } return instancia; }}
// Singleton con enum (mejor práctica)public enum SingletonEnum { INSTANCIA;
public void metodo() { // ... }}Cómo responder en entrevista:
“Singleton garantiza una sola instancia en toda la aplicación. La implementación con
enumes la mejor práctica: es thread-safe por naturaleza, resiste serialización y ataques por reflexión, y es la más simple. El double-checked locking convolatilees correcto pero más complejo. Casos de uso: connection pools, loggers, configuración.”
14. ¿Qué es inmutabilidad?
Section titled “14. ¿Qué es inmutabilidad?”¿Qué es? Objeto cuyo estado NO puede modificarse después de su creación.
¿Para qué sirve?
- Thread-safe por naturaleza (no hay estado mutable)
- Fácil de razonar y debuggear
- Puede usarse como clave en HashMap
- Cacheable de forma segura
Cómo crear una clase inmutable:
- Clase
final(no se puede extender) - Campos
private final - Sin setters
- Copias defensivas en constructor y getters
Ejemplo práctico:
// Clase inmutablepublic final class Inmutable { private final String valor; private final List<String> lista;
public Inmutable(String valor, List<String> lista) { this.valor = valor; this.lista = new ArrayList<>(lista); // Copia defensiva }
public String getValor() { return valor; }
public List<String> getLista() { return Collections.unmodifiableList(lista); // Vista inmutable }}
// String es inmutableString s = "Hola";s.concat(" Mundo"); // Crea nuevo String, no modifica sSystem.out.println(s); // "Hola"Cómo responder en entrevista:
“Una clase inmutable es thread-safe por naturaleza, no necesita sincronización. Se crea con clase
final, camposprivate final, sin setters y copias defensivas para colecciones mutables. Los ejemplos clásicos sonString,BigDecimaly las clases wrapper. Java 14+ tiene Records que generan clases inmutables automáticamente.”
15. ¿Cuál es la diferencia entre String, StringBuilder y StringBuffer?
Section titled “15. ¿Cuál es la diferencia entre String, StringBuilder y StringBuffer?”¿Qué es? Tres clases para manejar texto con diferentes características.
Diferencias:
| Característica | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutabilidad | Inmutable | Mutable | Mutable |
| Thread-safe | Sí (inmutable) | No | Sí |
| Performance | Lento para concatenación | Rápido | Más lento |
¿Cuál usar?
- String: Texto que no cambia
- StringBuilder: Concatenación en un solo thread (más común)
- StringBuffer: Concatenación multi-thread (raro)
Ejemplo práctico:
// String - crea nuevos objetosString s = "Hola";s = s + " Mundo"; // Nuevo objeto
// StringBuilder - modifica el mismo objetoStringBuilder sb = new StringBuilder("Hola");sb.append(" Mundo"); // Mismo objeto
// StringBuffer - thread-safeStringBuffer sbf = new StringBuffer("Hola");sbf.append(" Mundo");Cómo responder en entrevista:
“
Stringes inmutable: cada concatenación con+crea objetos en el heap, ineficiente en bucles.StringBuilderes mutable y rápido para concatenación en un solo thread.StringBufferes thread-safe pero más lento. En bucles siempre usoStringBuilder.StringBuffersolo en contextos realmente multi-thread.”
16. ¿Cuál es la jerarquía de Collections?
Section titled “16. ¿Cuál es la jerarquía de Collections?”Definición profesional:
El Collections Framework organiza las estructuras de datos en tres familias: List, Set y Queue, además de Map que no extiende Collection.
Ejemplo práctico:
// Collection (interface)// ├── List (interface) - ordenada, permite duplicados// │ ├── ArrayList// │ ├── LinkedList// │ └── Vector (legacy)// ├── Set (interface) - sin duplicados// │ ├── HashSet// │ ├── LinkedHashSet// │ └── TreeSet (ordenado)// └── Queue (interface) - FIFO// ├── LinkedList// ├── PriorityQueue// └── ArrayDeque
// Map (interface) - pares clave-valor// ├── HashMap// ├── LinkedHashMap// ├── TreeMap (ordenado)// └── Hashtable (legacy)Cómo responder en entrevista:
“El framework tiene tres ramas: List para listas ordenadas con duplicados (ArrayList por defecto), Set para conjuntos sin duplicados (HashSet por defecto), Queue para colas (ArrayDeque). Map no extiende Collection pero es parte del framework. Cada rama tiene implementaciones para distintas necesidades de rendimiento y orden.”
17. ¿Cuál es la diferencia entre HashMap y Hashtable?
Section titled “17. ¿Cuál es la diferencia entre HashMap y Hashtable?”¿Qué es? Dos implementaciones de Map con diferencias importantes.
Diferencias:
| HashMap | Hashtable |
|---|---|
| No sincronizado | Sincronizado |
| Permite null keys/values | No permite null |
| Más rápido | Más lento |
| Moderno | Legacy |
Recomendación: Usar HashMap para single-thread, ConcurrentHashMap para multi-thread.
18. ¿Cómo funciona HashMap internamente?
Section titled “18. ¿Cómo funciona HashMap internamente?”Definición profesional:
Usa un array de buckets. El hashCode() de la clave determina el bucket. Las colisiones se resuelven con lista enlazada, o árbol rojo-negro desde Java 8 si el bucket supera 8 elementos.
// HashMap: array de buckets + linked list (o árbol rojo-negro desde Java 8)// 1. hash = key.hashCode()// 2. índice = hash & (capacidad - 1)// 3. Colisión → lista enlazada; si >8 elementos → árbol rojo-negro O(log n)// Factor de carga por defecto: 0.75 (rehash cuando capacidad * 0.75 llena)Cómo responder en entrevista:
“HashMap usa un array de buckets. Calcula el bucket con
hash & (n-1). Las colisiones van a la lista enlazada del bucket. Desde Java 8, si un bucket supera 8 entradas se convierte en árbol rojo-negro: peor caso de O(n) pasa a O(log n). Es crucial implementarhashCode()yequals()correctamente en las claves.”
19. ¿Qué es un TreeMap?
Section titled “19. ¿Qué es un TreeMap?”¿Qué es? Implementación de Map que mantiene las claves ORDENADAS usando un árbol rojo-negro.
Características:
- Claves ordenadas (natural o con Comparator)
- O(log n) para todas las operaciones
- No permite claves null
- Las claves deben ser Comparable o proveer Comparator
¿Cuándo usarlo?
Cuando necesitas iterar en orden o usar métodos como firstKey(), lastKey(), headMap(), tailMap().
Ejemplo práctico:
// TreeMap - Map ordenado por claves (Red-Black Tree)TreeMap<String, Integer> mapa = new TreeMap<>();mapa.put("Banana", 2);mapa.put("Apple", 1);mapa.put("Cherry", 3);
// Iteración en orden alfabéticofor (String key : mapa.keySet()) { System.out.println(key); // Apple, Banana, Cherry}
// Métodos útilesmapa.firstKey(); // "Apple"mapa.lastKey(); // "Cherry"mapa.headMap("C"); // Claves < "C"mapa.tailMap("B"); // Claves >= "B"Cómo responder en entrevista:
“
TreeMapmantiene las claves ordenadas con árbol rojo-negro, todas las operaciones son O(log n). Lo uso cuando necesito iterar en orden o aprovecharNavigableMapconheadMap,tailMap,floorKey,ceilingKey. No permitenullcomo clave. Si no necesito orden,HashMapes más eficiente con O(1).“
20. ¿Cuál es la diferencia entre HashSet y TreeSet?
Section titled “20. ¿Cuál es la diferencia entre HashSet y TreeSet?”¿Qué es? Dos implementaciones de Set con diferentes características.
Diferencias:
| HashSet | TreeSet |
|---|---|
| No ordenado | Ordenado |
| O(1) para operaciones | O(log n) |
| Permite un null | No permite null |
| Usa HashMap internamente | Usa TreeMap internamente |
¿Cuál usar?
- HashSet: Cuando no necesitas orden (más rápido)
- TreeSet: Cuando necesitas elementos ordenados
21. ¿Qué es un Iterator?
Section titled “21. ¿Qué es un Iterator?”Definición profesional: Patrón de diseño que proporciona un mecanismo uniforme para recorrer colecciones. Es el único modo seguro de eliminar elementos durante la iteración.
List<String> lista = new ArrayList<>(Arrays.asList("A", "B", "C"));
// Iterator - único modo seguro de eliminar durante iteraciónIterator<String> it = lista.iterator();while (it.hasNext()) { String elem = it.next(); if (elem.equals("B")) it.remove(); // ✅ Seguro // lista.remove(elem); // ❌ ConcurrentModificationException}
// ListIterator - bidireccional, permite modificarListIterator<String> lit = lista.listIterator();while (lit.hasNext()) { lit.set(lit.next().toLowerCase()); // Modificar elemento actual}Cómo responder en entrevista:
“
IteratorproporcionahasNext(),next()yremove(). Es el único modo seguro de eliminar durante iteración: hacerlo directamente en la lista lanzaConcurrentModificationException.ListIteratorextiendeIteratorcon soporte bidireccional y métodosset()yadd(). El for-each usa Iterator internamente.”
22. ¿Qué es fail-fast vs fail-safe?
Section titled “22. ¿Qué es fail-fast vs fail-safe?”¿Qué es? Dos comportamientos de iteradores cuando la colección se modifica durante iteración.
Diferencias:
| Fail-fast | Fail-safe |
|---|---|
| Lanza ConcurrentModificationException | No lanza excepción |
| ArrayList, HashMap, etc. | CopyOnWriteArrayList, ConcurrentHashMap |
| Detecta modificación inmediatamente | Trabaja con copia |
Ejemplo práctico:
// Fail-fast: lanza ConcurrentModificationExceptionList<String> lista = new ArrayList<>();lista.add("A");for (String s : lista) { lista.add("B"); // ConcurrentModificationException}
// Fail-safe: trabaja con copiaList<String> listaConcurrente = new CopyOnWriteArrayList<>();listaConcurrente.add("A");for (String s : listaConcurrente) { listaConcurrente.add("B"); // OK, pero no ve los nuevos}Cómo responder en entrevista:
“Fail-fast detecta modificaciones concurrentes mediante un contador
modCounty lanzaConcurrentModificationException. Es para single-thread: ayuda a detectar bugs. Fail-safe (CopyOnWriteArrayList, ConcurrentHashMap) es para multi-thread: opera sobre copia o con locks finos, sin excepciones pero con mayor coste de memoria.”
23. ¿Qué es Comparable vs Comparator?
Section titled “23. ¿Qué es Comparable vs Comparator?”¿Qué es? Dos interfaces para definir cómo ordenar objetos.
Diferencias:
| Comparable | Comparator |
|---|---|
| Implementado EN la clase | Externo a la clase |
Método compareTo() | Método compare() |
| Un solo orden (natural) | Múltiples órdenes posibles |
java.lang | java.util |
¿Cuál usar?
- Comparable: Para el orden “por defecto” de la clase
- Comparator: Para órdenes alternativos o clases que no puedes modificar
Ejemplo práctico:
// Comparable - orden natural (en la clase)public class Persona implements Comparable<Persona> { private String nombre; private int edad;
@Override public int compareTo(Persona otra) { return this.nombre.compareTo(otra.nombre); }}
// Comparator - orden personalizado (externo)Comparator<Persona> porEdad = (p1, p2) -> Integer.compare(p1.getEdad(), p2.getEdad());
Comparator<Persona> porNombreDesc = Comparator.comparing(Persona::getNombre).reversed();
// UsoList<Persona> personas = new ArrayList<>();Collections.sort(personas); // Usa ComparableCollections.sort(personas, porEdad); // Usa Comparatorpersonas.sort(porNombreDesc);Cómo responder en entrevista:
“
Comparablese implementa dentro de la propia clase concompareTo()y define el orden natural.Comparatores externo concompare()y permite múltiples criterios. UsoComparablepara el orden por defecto de la clase yComparatorpara órdenes alternativos o cuando no puedo modificar la clase.”
24. ¿Qué es un PriorityQueue?
Section titled “24. ¿Qué es un PriorityQueue?”¿Qué es? Cola que ordena elementos por prioridad usando un heap.
Características:
- Por defecto: min-heap (menor primero)
- O(log n) para inserción y extracción
- No permite null
- No es thread-safe
¿Para qué sirve?
- Algoritmos de Dijkstra
- Scheduling de tareas
- Encontrar k elementos más grandes/pequeños
Ejemplo práctico:
// PriorityQueue - cola con prioridad (heap)PriorityQueue<Integer> pq = new PriorityQueue<>(); // Min-heappq.add(5);pq.add(1);pq.add(3);
System.out.println(pq.poll()); // 1 (menor primero)System.out.println(pq.poll()); // 3System.out.println(pq.poll()); // 5
// Max-heapPriorityQueue<Integer> maxPq = new PriorityQueue<>( Collections.reverseOrder());Cómo responder en entrevista:
“
PriorityQueueimplementa un heap binario dondepoll()siempre extrae el elemento de mayor prioridad en O(log n). Por defecto es min-heap. ConCollections.reverseOrder()se convierte en max-heap. Lo uso en algoritmos de grafos (Dijkstra), scheduling de tareas y problemas de k-th element.”
25. ¿Qué es un Deque?
Section titled “25. ¿Qué es un Deque?”¿Qué es? Double-Ended Queue - cola que permite insertar y eliminar en AMBOS extremos.
¿Para qué sirve?
- Como Stack (LIFO):
push(),pop() - Como Queue (FIFO):
addLast(),removeFirst() - Implementaciones:
ArrayDeque(más rápido),LinkedList
Nota: ArrayDeque es preferido sobre Stack (legacy) y LinkedList para stacks/queues.
Ejemplo práctico:
// Deque - Double Ended QueueDeque<String> deque = new ArrayDeque<>();
// Como Stack (LIFO)deque.push("A");deque.push("B");deque.pop(); // "B"
// Como Queue (FIFO)deque.addLast("C");deque.addLast("D");deque.removeFirst(); // "A"
// Métodosdeque.addFirst("X");deque.addLast("Y");deque.peekFirst();deque.peekLast();Cómo responder en entrevista:
“
Dequees una cola de doble extremo que reemplaza tanto aStackcomo aLinkedListpara uso como cola.ArrayDequees la implementación preferida: más eficiente en tiempo y memoria queLinkedList. Lo uso como Stack conpush/popy como Queue conofferLast/pollFirst. No permitenull.”
26. ¿Cómo crear un Thread?
Section titled “26. ¿Cómo crear un Thread?”Definición profesional:
Java ofrece tres mecanismos para crear threads: extender Thread, implementar Runnable, o usar lambda. En producción se prefiere ExecutorService.
Ejemplo práctico:
// Opción 1: Extender Threadclass MiThread extends Thread { @Override public void run() { System.out.println("Ejecutando en: " + getName()); }}
// Opción 2: Implementar Runnable (preferido)class MiRunnable implements Runnable { @Override public void run() { System.out.println("Ejecutando runnable"); }}
// Opción 3: LambdaRunnable lambda = () -> System.out.println("Lambda");
// Usonew MiThread().start();new Thread(new MiRunnable()).start();new Thread(lambda).start();Cómo responder en entrevista:
“Hay tres formas: extender
Thread(limita herencia), implementarRunnable(preferido, más flexible) o usar lambda. En producción evito crear threads directamente; usoExecutorServicepara gestionar un pool. Siempre llamostart(), nuncarun()directamente.”
27. ¿Cuál es la diferencia entre start() y run()?
Section titled “27. ¿Cuál es la diferencia entre start() y run()?”¿Qué es? Dos métodos de Thread con comportamientos muy diferentes.
Diferencias:
- start(): Crea un NUEVO thread y ejecuta
run()en él - run(): Ejecuta en el thread ACTUAL (no crea nuevo thread)
Error común: Llamar run() directamente no crea concurrencia.
Ejemplo práctico:
Thread t = new Thread(() -> { System.out.println("Thread: " + Thread.currentThread().getName());});
// start() - crea nuevo thread y ejecuta run()t.start(); // Imprime: Thread: Thread-0
// run() - ejecuta en el thread actual (NO crea nuevo thread)t.run(); // Imprime: Thread: mainCómo responder en entrevista:
“
start()crea un nuevo thread en la JVM y luego llama arun()en él: hay concurrencia real.run()simplemente ejecuta el método en el thread actual como una llamada normal. Llamarrun()directamente es el error más común con threads: compila y ejecuta sin error pero no hay paralelismo.”
28. ¿Qué son los estados de un Thread?
Section titled “28. ¿Qué son los estados de un Thread?”¿Qué es? Un Thread pasa por 6 estados durante su ciclo de vida.
Estados:
- NEW: Creado pero no iniciado
- RUNNABLE: Ejecutándose o listo para ejecutar
- BLOCKED: Esperando un lock (synchronized)
- WAITING: Esperando indefinidamente (wait, join)
- TIMED_WAITING: Esperando con timeout (sleep, wait con tiempo)
- TERMINATED: Finalizado
Ejemplo práctico:
// Estados de Thread// NEW - creado pero no iniciadoThread t = new Thread(() -> {});
// RUNNABLE - ejecutándose o listo para ejecutart.start();
// BLOCKED - esperando un locksynchronized(objeto) { }
// WAITING - esperando indefinidamenteobject.wait();thread.join();
// TIMED_WAITING - esperando con timeoutThread.sleep(1000);object.wait(1000);
// TERMINATED - finalizado// Después de que run() terminaCómo responder en entrevista:
“Un Thread tiene 6 estados: NEW al crear, RUNNABLE al iniciar, BLOCKED esperando un lock de
synchronized, WAITING esperando indefinidamente conwait/join, TIMED_WAITING con timeout, y TERMINATED al finalizar. Conjstackpuedo ver el estado de todos los threads en producción para detectar deadlocks.”
29. ¿Qué es wait(), notify() y notifyAll()?
Section titled “29. ¿Qué es wait(), notify() y notifyAll()?”¿Qué es? Métodos de Object para comunicación entre threads.
Métodos:
- wait(): Libera el lock y espera hasta ser notificado
- notify(): Despierta UN thread que está esperando
- notifyAll(): Despierta TODOS los threads esperando
Requisito: Deben usarse DENTRO de un bloque synchronized.
Ejemplo práctico - Productor/Consumidor:
public class ProductorConsumidor { private List<Integer> buffer = new ArrayList<>(); private int capacidad = 10;
public synchronized void producir(int valor) throws InterruptedException { while (buffer.size() == capacidad) { wait(); // Espera hasta que haya espacio } buffer.add(valor); notifyAll(); // Notifica a consumidores }
public synchronized int consumir() throws InterruptedException { while (buffer.isEmpty()) { wait(); // Espera hasta que haya elementos } int valor = buffer.remove(0); notifyAll(); // Notifica a productores return valor; }}Cómo responder en entrevista:
“
wait()libera el lock y pone el thread en WAITING hasta que otro llamenotify()/notifyAll().notify()despierta un thread arbitrario;notifyAll()despierta todos. Siempre usowhileen lugar deifpara re-verificar la condición al despertar (spurious wakeups). Deben estar dentro desynchronized.”
30. ¿Qué es un deadlock?
Section titled “30. ¿Qué es un deadlock?”¿Qué es? Situación donde dos o más threads están bloqueados esperando recursos que el otro tiene.
Condiciones para deadlock:
- Exclusión mutua
- Hold and wait
- No preemption
- Espera circular
Prevención:
- Ordenar la adquisición de locks
- Usar timeout con
tryLock() - Evitar locks anidados
Ejemplo práctico:
// Deadlock - dos threads esperando recursos del otroObject lock1 = new Object();Object lock2 = new Object();
Thread t1 = new Thread(() -> { synchronized(lock1) { Thread.sleep(100); synchronized(lock2) { } // Espera lock2 }});
Thread t2 = new Thread(() -> { synchronized(lock2) { Thread.sleep(100); synchronized(lock1) { } // Espera lock1 }});
// Prevención: ordenar locks, usar timeout, usar tryLock()Cómo responder en entrevista:
“Un deadlock ocurre cuando dos threads se bloquean mutuamente esperando locks que el otro tiene. Se previene adquiriendo locks siempre en el mismo orden, usando
tryLock()con timeout deReentrantLock, o evitando locks anidados. Para detectarlos en producción usojstackpara obtener thread dumps.”
31. ¿Qué es ExecutorService?
Section titled “31. ¿Qué es ExecutorService?”¿Qué es? Framework para manejar un pool de threads reutilizables.
¿Para qué sirve?
- Evitar crear/destruir threads constantemente (costoso)
- Limitar el número de threads concurrentes
- Manejar tareas asíncronas de forma ordenada
Métodos principales:
execute(Runnable): Ejecutar sin resultadosubmit(Callable): Ejecutar con resultado (Future)shutdown(): Cerrar el pool
Ejemplo práctico:
// ExecutorService - pool de threadsExecutorService executor = Executors.newFixedThreadPool(4);
// Ejecutar Runnableexecutor.execute(() -> System.out.println("Tarea"));
// Ejecutar Callable (retorna valor)Future<Integer> future = executor.submit(() -> { Thread.sleep(1000); return 42;});
int resultado = future.get(); // Bloquea hasta obtener resultado
// Cerrar executorexecutor.shutdown();executor.awaitTermination(5, TimeUnit.SECONDS);Cómo responder en entrevista:
“
ExecutorServicegestiona un pool de threads reutilizables.execute()para tareas sin resultado,submit()paraCallableconFuturecomo resultado. Siempre cierro conshutdown()yawaitTermination(). En Spring Boot configuroThreadPoolTaskExecutorconcorePoolSize,maxPoolSizeyqueueCapacitysegún la carga esperada.”
32. ¿Cuáles son los tipos de ExecutorService?
Section titled “32. ¿Cuáles son los tipos de ExecutorService?”¿Qué es? Diferentes implementaciones de ExecutorService para distintos casos de uso.
Tipos:
- FixedThreadPool: Número fijo de threads (para carga predecible)
- CachedThreadPool: Crea threads según demanda (para muchas tareas cortas)
- SingleThreadExecutor: Un solo thread (para tareas secuenciales)
- ScheduledThreadPool: Para tareas programadas/periódicas
Ejemplo práctico:
// Fixed Thread Pool - número fijo de threadsExecutorService fixed = Executors.newFixedThreadPool(4);
// Cached Thread Pool - crea threads según necesidadExecutorService cached = Executors.newCachedThreadPool();
// Single Thread - un solo threadExecutorService single = Executors.newSingleThreadExecutor();
// Scheduled - para tareas programadasScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2);scheduled.scheduleAtFixedRate( () -> System.out.println("Cada 5 segundos"), 0, 5, TimeUnit.SECONDS);Cómo responder en entrevista:
“
FixedThreadPoolpara carga predecible con límite de threads.CachedThreadPoolpara muchas tareas cortas (cuidado: puede crear miles de threads).SingleThreadExecutorpara serializar tareas.ScheduledThreadPoolpara cron jobs. En Java 21, los Virtual Threads permiten millones de threads ligeros para workloads I/O.”
33. ¿Qué es CompletableFuture?
Section titled “33. ¿Qué es CompletableFuture?”¿Qué es? Future mejorado (Java 8+) para programación asíncrona no bloqueante.
¿Para qué sirve?
- Encadenar operaciones asíncronas (
thenApply,thenCompose) - Combinar múltiples futures (
thenCombine,allOf) - Manejar errores (
exceptionally,handle)
Ventajas sobre Future:
- No bloqueante
- Composable
- Manejo de errores integrado
Ejemplo práctico:
// CompletableFuture - programación asíncronaCompletableFuture<String> future = CompletableFuture .supplyAsync(() -> { // Tarea asíncrona return "Resultado"; }) .thenApply(s -> s.toUpperCase()) .thenApply(s -> "Procesado: " + s);
// Combinar futuresCompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Hola");CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "Mundo");
CompletableFuture<String> combinado = f1.thenCombine(f2, (s1, s2) -> s1 + " " + s2);
// Esperar múltiplesCompletableFuture.allOf(f1, f2).join();Cómo responder en entrevista:
“
CompletableFuturemejoraFutureañadiendo composición no bloqueante.thenApplytransforma,thenComposeencadena futures async,thenCombinecombina dos yallOfespera varios en paralelo.exceptionally/handlegestionan errores. Es la base para aplicaciones reactivas antes de Spring WebFlux.”
34. ¿Qué es un Lock vs synchronized?
Section titled “34. ¿Qué es un Lock vs synchronized?”¿Qué es? Dos mecanismos para sincronización de threads.
Diferencias:
| synchronized | Lock |
|---|---|
| Simple, automático | Más flexible |
| No timeout | tryLock() con timeout |
| Solo exclusivo | ReadWriteLock disponible |
| Libera automáticamente | Requiere unlock() manual |
Recomendación: Usar synchronized por defecto, Lock cuando necesites más control.
Ejemplo práctico:
// synchronized - simple pero limitadosynchronized(objeto) { // código crítico}
// Lock - más flexibleLock lock = new ReentrantLock();
lock.lock();try { // código crítico} finally { lock.unlock();}
// tryLock - intenta sin bloquearif (lock.tryLock(1, TimeUnit.SECONDS)) { try { // código } finally { lock.unlock(); }}
// ReadWriteLock - múltiples lectores, un escritorReadWriteLock rwLock = new ReentrantReadWriteLock();rwLock.readLock().lock();rwLock.writeLock().lock();Cómo responder en entrevista:
“
synchronizedes suficiente para la mayoría de casos.ReentrantLockañadetryLockcon timeout para evitar deadlocks,lockInterruptibly()para threads cancelables, yReadWriteLockpara mayor concurrencia en lecturas. Siempre entry/finallypara garantizar elunlockincluso con excepciones.”
35. ¿Qué son las clases Atomic?
Section titled “35. ¿Qué son las clases Atomic?”¿Qué es? Clases que proporcionan operaciones atómicas sin necesidad de synchronized.
Clases principales:
AtomicInteger,AtomicLong: Contadores atómicosAtomicBoolean: Flag atómicoAtomicReference<T>: Referencia atómica a objeto
¿Cómo funcionan? Usan CAS (Compare-And-Swap) internamente, más eficiente que locks.
Ejemplo práctico:
// Operaciones atómicas sin synchronizedAtomicInteger contador = new AtomicInteger(0);
contador.incrementAndGet(); // ++contadorcontador.getAndIncrement(); // contador++contador.addAndGet(5); // contador += 5contador.compareAndSet(5, 10); // Si es 5, cambiar a 10
// AtomicReference para objetosAtomicReference<String> ref = new AtomicReference<>("inicial");ref.compareAndSet("inicial", "nuevo");Cómo responder en entrevista:
“Las clases Atomic usan instrucciones CAS del CPU: más eficientes que
synchronizedpara operaciones simples porque no hay bloqueo de threads.AtomicIntegerpara contadores,AtomicBooleanpara flags,AtomicReferencepara objetos.LongAdderes mejor queAtomicLongen alta contención porque distribuye el trabajo.”
36. ¿Qué es una expresión Lambda?
Section titled “36. ¿Qué es una expresión Lambda?”Definición profesional:
Función anónima que implementa una interfaz funcional. Sintaxis: (parámetros) -> expresión o (parámetros) -> { bloque }.
Ejemplo práctico:
// Lambda - función anónima// (parámetros) -> expresión// (parámetros) -> { statements }
// Sin parámetrosRunnable r = () -> System.out.println("Hola");
// Un parámetroConsumer<String> c = s -> System.out.println(s);
// Múltiples parámetrosBiFunction<Integer, Integer, Integer> suma = (a, b) -> a + b;
// Con bloqueComparator<String> comp = (s1, s2) -> { int len1 = s1.length(); int len2 = s2.length(); return Integer.compare(len1, len2);};Cómo responder en entrevista:
“Una lambda es una función anónima que implementa una interfaz funcional. La sintaxis es
(params) -> expresión. Simplifican el código al reemplazar clases anónimas verbosas. Las variables capturadas del scope externo deben sereffectively final. Son el fundamento de Streams y la programación funcional en Java 8+.“
37. ¿Qué es una interfaz funcional?
Section titled “37. ¿Qué es una interfaz funcional?”¿Qué es? Interfaz con UN SOLO método abstracto (SAM - Single Abstract Method).
Características:
- Puede tener métodos
defaultystatic - Anotación
@FunctionalInterfaceopcional pero recomendada - Puede ser implementada con lambda
Interfaces funcionales comunes:
Function<T, R>: T → RConsumer<T>: T → voidSupplier<T>: () → TPredicate<T>: T → boolean
Ejemplo práctico:
// Interfaz con un solo método abstracto@FunctionalInterfacepublic interface MiInterfaz { int calcular(int x);
// Puede tener métodos default y static default void metodoDefault() {} static void metodoStatic() {}}
// Interfaces funcionales comunes:// Function<T, R> - T -> R// Consumer<T> - T -> void// Supplier<T> - () -> T// Predicate<T> - T -> boolean// BiFunction<T,U,R> - (T, U) -> RCómo responder en entrevista:
“Una interfaz funcional tiene exactamente un método abstracto.
@FunctionalInterfaceverifica esto y da error de compilación si no se cumple. Las más usadas del JDK:Functionpara transformar,Consumerpara efectos,Supplierpara fábricas,Predicatepara filtros. Cualquier SAM puede usarse con lambda aunque no tenga la anotación.”
38. ¿Qué es Method Reference?
Section titled “38. ¿Qué es Method Reference?”¿Qué es? Atajo para lambdas que solo llaman a un método existente.
Tipos:
Clase::metodoEstatico- Método estáticoobjeto::metodo- Método de instancia específicaClase::metodo- Método de instancia (primer param es el objeto)Clase::new- Constructor
Ejemplo práctico:
// Method Reference - atajo para lambdas simplesList<String> lista = Arrays.asList("a", "b", "c");
// Lambdalista.forEach(s -> System.out.println(s));
// Method Referencelista.forEach(System.out::println);
// Tipos:// Estático: Clase::metodoEstaticoInteger::parseInt
// Instancia de objeto: objeto::metodoString s = "hola";Supplier<Integer> len = s::length;
// Instancia de clase: Clase::metodoFunction<String, Integer> f = String::length;
// Constructor: Clase::newSupplier<ArrayList<String>> sup = ArrayList::new;Cómo responder en entrevista:
“Un Method Reference es azúcar sintáctica para una lambda que solo llama a un método. Hay cuatro formas: método estático (
Integer::parseInt), método de instancia de objeto específico (objeto::metodo), método de instancia de clase (String::toUpperCase) y constructor (Clase::new). Mejoran la legibilidad cuando la lambda es trivial.”
39. ¿Qué es Stream API?
Section titled “39. ¿Qué es Stream API?”¿Qué es? API para procesar secuencias de elementos de forma declarativa.
Estructura:
- Fuente: Collection, array, generador
- Operaciones intermedias: filter, map, sorted (lazy)
- Operación terminal: collect, forEach, reduce (ejecuta el pipeline)
Características:
- No modifica la fuente original
- Lazy evaluation (solo ejecuta cuando hay terminal)
- Se consume una sola vez
Ejemplo práctico:
List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "María");
// Stream pipeline: source -> operaciones intermedias -> operación terminalList<String> resultado = nombres.stream() .filter(n -> n.length() > 3) // Intermedia .map(String::toUpperCase) // Intermedia .sorted() // Intermedia .collect(Collectors.toList()); // Terminal
// Operaciones intermedias: filter, map, flatMap, sorted, distinct, limit, skip// Operaciones terminales: collect, forEach, reduce, count, findFirst, anyMatchCómo responder en entrevista:
“La Stream API procesa datos de forma declarativa. Las operaciones intermedias son lazy: no ejecutan hasta que hay una terminal. Las terminales (
collect,forEach,reduce) disparan el pipeline. Los streams no modifican la fuente original y se consumen una sola vez. Para primitivos usoIntStream/LongStreampara evitar autoboxing.”
40. ¿Cuál es la diferencia entre map() y flatMap()?
Section titled “40. ¿Cuál es la diferencia entre map() y flatMap()?”¿Qué es? Dos operaciones de transformación en Streams.
Diferencias:
- map(): Transforma cada elemento 1:1 (T → R)
- flatMap(): Transforma y APLANA (T → Stream de R)
¿Cuándo usar flatMap? Cuando tienes streams anidados o cada elemento produce múltiples resultados.
Ejemplo práctico:
// map() - transforma cada elementoList<String> palabras = Arrays.asList("Hola", "Mundo");List<Integer> longitudes = palabras.stream() .map(String::length) .collect(Collectors.toList()); // [4, 5]
// flatMap() - aplana streams anidadosList<List<Integer>> listas = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4));
List<Integer> plana = listas.stream() .flatMap(List::stream) .collect(Collectors.toList()); // [1, 2, 3, 4]Cómo responder en entrevista:
“
map()transforma elemento a elemento con relación 1:1.flatMap()transforma cada elemento en un Stream y los fusiona: relación 1:N aplanada. Lo uso cuando tengo estructuras anidadas: lista de listas u objetos con colecciones internas. Simap()devuelve un Stream,flatMap()lo aplana automáticamente.”
41. ¿Qué es reduce()?
Section titled “41. ¿Qué es reduce()?”¿Qué es? Operación terminal que combina todos los elementos en uno solo.
Sintaxis:
reduce(valorInicial, (acumulador, elemento) -> resultado)Casos de uso:
- Sumar elementos
- Encontrar máximo/mínimo
- Concatenar strings
- Cualquier operación de agregación
Ejemplo práctico:
List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5);
// reduce - combina elementos en uno soloint suma = numeros.stream() .reduce(0, (a, b) -> a + b); // 15
int producto = numeros.stream() .reduce(1, (a, b) -> a * b); // 120
// Sin valor inicial (retorna Optional)Optional<Integer> max = numeros.stream() .reduce(Integer::max);Cómo responder en entrevista:
“
reduce()agrega todos los elementos en un único resultado. Con valor identidad retornaTdirectamente; sin él retornaOptionalporque el stream puede estar vacío. Para sumas, máximos o promedios de primitivos prefieromapToInt().sum()oCollectors.summingInt()que son más legibles y evitan autoboxing.”
42. ¿Qué son los Collectors?
Section titled “42. ¿Qué son los Collectors?”¿Qué es?
Clase de utilidades para la operación terminal collect().
Collectors comunes:
toList(),toSet(): Convertir a coleccióntoMap(): Convertir a MapgroupingBy(): Agrupar por criteriopartitioningBy(): Dividir en dos grupos (true/false)joining(): Concatenar stringscounting(),summingInt(): Estadísticas
Ejemplo práctico:
List<Persona> personas = // ...
// toList, toSetList<String> nombres = personas.stream() .map(Persona::getNombre) .collect(Collectors.toList());
// toMapMap<Integer, String> mapa = personas.stream() .collect(Collectors.toMap( Persona::getId, Persona::getNombre ));
// groupingByMap<String, List<Persona>> porCiudad = personas.stream() .collect(Collectors.groupingBy(Persona::getCiudad));
// partitioningByMap<Boolean, List<Persona>> mayores = personas.stream() .collect(Collectors.partitioningBy(p -> p.getEdad() >= 18));
// joiningString nombresUnidos = personas.stream() .map(Persona::getNombre) .collect(Collectors.joining(", ")); // "Ana, Juan, Pedro"Cómo responder en entrevista:
“
Collectorsproporciona implementaciones paracollect(). Las más usadas:toList/toSetpara colecciones,groupingBypara agrupar (equivalente a GROUP BY de SQL),partitioningBypara dividir en dos grupos,joiningpara concatenar strings con delimitador,countingysummingIntpara estadísticas.”
43. ¿Qué es Optional?
Section titled “43. ¿Qué es Optional?”¿Qué es? Contenedor que puede o no contener un valor no-null.
¿Para qué sirve?
- Evitar
nullyNullPointerException - Hacer explícito que un valor puede estar ausente
- API más expresiva
Métodos principales:
orElse(),orElseGet(),orElseThrow(): Valor por defectomap(),flatMap(): Transformarfilter(): FiltrarifPresent(): Ejecutar si tiene valor
Ejemplo práctico:
// Optional - contenedor que puede o no tener valorOptional<String> opcional = Optional.of("valor");Optional<String> vacio = Optional.empty();Optional<String> nullable = Optional.ofNullable(posibleNull);
// Obtener valorString valor = opcional.get(); // Lanza excepción si vacíoString valorODefault = opcional.orElse("default");String valorOSupplier = opcional.orElseGet(() -> calcularDefault());String valorOException = opcional.orElseThrow(() -> new RuntimeException());
// Operacionesopcional.isPresent(); // true si tiene valoropcional.ifPresent(v -> System.out.println(v));opcional.map(String::toUpperCase);opcional.filter(s -> s.length() > 3);opcional.flatMap(this::buscarOtro);Cómo responder en entrevista:
“
Optionalhace explícito que un valor puede estar ausente. UsoofNullable()para valores potencialmente null,orElse()para valor por defecto barato yorElseGet()con Supplier para valor costoso. Evitoget()sinisPresent()previo.Optionales para retornos de método, no para campos de clase ni parámetros.”
44. ¿Qué es un parallel stream?
Section titled “44. ¿Qué es un parallel stream?”¿Qué es? Stream que procesa elementos en paralelo usando múltiples threads.
¿Cómo funciona? Usa el ForkJoinPool común para dividir el trabajo.
¿Cuándo usarlo?
- Grandes volúmenes de datos
- Operaciones CPU-intensivas
- Operaciones independientes entre sí
Cuidado con:
- Operaciones con estado compartido
- Colecciones pequeñas (overhead > beneficio)
- Orden de elementos
Ejemplo práctico:
List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Stream paralelo - usa múltiples threadsint suma = numeros.parallelStream() .filter(n -> n % 2 == 0) .mapToInt(Integer::intValue) .sum();
// Convertir a paralelonumeros.stream().parallel();
// Cuidado con:// - Operaciones con estado// - Orden de elementos// - Overhead para colecciones pequeñasCómo responder en entrevista:
“Los parallel streams dividen el trabajo entre threads del
ForkJoinPool. Son útiles para operaciones CPU-intensivas con grandes volúmenes de datos independientes. Evito usarlos con colecciones pequeñas (overhead supera el beneficio), con estado compartido (race conditions) o en operaciones I/O. Siempre mido con benchmark antes de adoptar.”
45. ¿Cuál es la diferencia entre Stream y Collection?
Section titled “45. ¿Cuál es la diferencia entre Stream y Collection?”¿Qué es? Dos formas de trabajar con secuencias de datos con filosofías diferentes.
Diferencias:
| Stream | Collection |
|---|---|
| No almacena datos | Almacena datos |
| Lazy evaluation | Eager evaluation |
| Puede ser infinito | Finito |
| Se consume una vez | Reutilizable |
| Operaciones funcionales | Operaciones imperativas |
JDBC y Base de Datos (46-50)
Section titled “JDBC y Base de Datos (46-50)”46. ¿Qué es JDBC?
Section titled “46. ¿Qué es JDBC?”Definición profesional: Java Database Connectivity: API estándar de Java para conectar y ejecutar operaciones sobre bases de datos relacionales mediante un driver específico.
Ejemplo práctico:
// JDBC - Java Database Connectivity// 1. Cargar driverClass.forName("com.mysql.cj.jdbc.Driver");
// 2. Establecer conexiónConnection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/midb", "usuario", "password");
// 3. Crear statementStatement stmt = conn.createStatement();
// 4. Ejecutar queryResultSet rs = stmt.executeQuery("SELECT * FROM usuarios");
// 5. Procesar resultadoswhile (rs.next()) { int id = rs.getInt("id"); String nombre = rs.getString("nombre");}
// 6. Cerrar recursosrs.close();stmt.close();conn.close();Cómo responder en entrevista:
“JDBC es la API estándar para conectar Java con bases de datos. Los pasos son: obtener conexión del
DriverManager/DataSource, crearPreparedStatement, ejecutar, procesarResultSety cerrar recursos. Siempre usotry-with-resourcespara garantizar el cierre. En producción uso un connection pool como HikariCP, no conexiones directas.”
47. ¿Qué es PreparedStatement?
Section titled “47. ¿Qué es PreparedStatement?”¿Qué es?
Statement precompilado con parámetros marcados con ?.
Ventajas:
- Previene SQL injection (los parámetros se escapan automáticamente)
- Mejor performance para queries repetidas (se compila una vez)
- Más legible que concatenar strings
Siempre usar PreparedStatement en lugar de Statement con datos de usuario.
Ejemplo práctico:
// PreparedStatement - previene SQL injectionString sql = "SELECT * FROM usuarios WHERE nombre = ? AND edad > ?";PreparedStatement pstmt = conn.prepareStatement(sql);pstmt.setString(1, nombre);pstmt.setInt(2, edad);ResultSet rs = pstmt.executeQuery();
// Para INSERT/UPDATE/DELETEString insertSql = "INSERT INTO usuarios (nombre, edad) VALUES (?, ?)";PreparedStatement insertStmt = conn.prepareStatement(insertSql);insertStmt.setString(1, "Juan");insertStmt.setInt(2, 25);int filasAfectadas = insertStmt.executeUpdate();Cómo responder en entrevista:
“
PreparedStatementpreviene SQL injection porque los parámetros se escapan automáticamente antes de enviarse a la BD. Además el driver puede cachear el plan de ejecución para queries repetidas. Nunca concateno valores de usuario en queries SQL directamente.”
48. ¿Qué es una transacción en JDBC?
Section titled “48. ¿Qué es una transacción en JDBC?”¿Qué es? Grupo de operaciones que se ejecutan como una unidad atómica.
Propiedades ACID:
- Atomicity: Todo o nada
- Consistency: Estado válido antes y después
- Isolation: Transacciones no interfieren
- Durability: Cambios permanentes
Control:
setAutoCommit(false): Iniciar transaccióncommit(): Confirmar cambiosrollback(): Revertir cambios
Ejemplo práctico:
Connection conn = null;try { conn = DriverManager.getConnection(url, user, pass); conn.setAutoCommit(false); // Iniciar transacción
// Operaciones stmt1.executeUpdate("UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1"); stmt2.executeUpdate("UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2");
conn.commit(); // Confirmar transacción} catch (SQLException e) { if (conn != null) { conn.rollback(); // Revertir en caso de error }} finally { if (conn != null) { conn.setAutoCommit(true); conn.close(); }}Cómo responder en entrevista:
“Una transacción garantiza ACID: Atomicity (todo o nada), Consistency (estado válido antes y después), Isolation (no afecta otras transacciones) y Durability (los cambios persisten). En JDBC desactivo
autoCommit, ejecuto las operaciones y hagocommitorollbacken el catch. En Spring prefiero@Transactionalque gestiona esto automáticamente.”
49. ¿Qué es un Connection Pool?
Section titled “49. ¿Qué es un Connection Pool?”¿Qué es? Conjunto de conexiones a base de datos que se reutilizan.
¿Para qué sirve?
- Evitar crear/cerrar conexiones constantemente (costoso)
- Limitar número máximo de conexiones
- Mejorar performance significativamente
Implementaciones populares:
- HikariCP: El más rápido y recomendado
- Apache DBCP: Clásico
- C3P0: Legacy
Ejemplo práctico con HikariCP:
// Connection Pool - reutiliza conexiones// Con HikariCP (popular)HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:mysql://localhost:3306/midb");config.setUsername("usuario");config.setPassword("password");config.setMaximumPoolSize(10);
HikariDataSource dataSource = new HikariDataSource(config);
// Obtener conexión del pooltry (Connection conn = dataSource.getConnection()) { // usar conexión} // Se devuelve al pool automáticamenteCómo responder en entrevista:
“Crear una conexión JDBC es costoso: establece comunicación de red, autentica y negocia el protocolo. Un connection pool mantiene conexiones listas para ser prestadas. HikariCP es el más rápido y es el default en Spring Boot. Los parámetros clave son
maxPoolSize(límite de conexiones simultáneas) yconnectionTimeout.”
50. ¿Qué es el patrón DAO?
Section titled “50. ¿Qué es el patrón DAO?”¿Qué es? Data Access Object - patrón que separa la lógica de acceso a datos de la lógica de negocio.
¿Para qué sirve?
- Separación de responsabilidades: La capa de negocio no conoce detalles de BD
- Testabilidad: Fácil de mockear
- Flexibilidad: Cambiar BD sin afectar negocio
Estructura:
- Interface con operaciones CRUD
- Implementación con lógica de BD
Ejemplo práctico:
// DAO - Data Access Objectpublic interface UsuarioDAO { Usuario findById(int id); List<Usuario> findAll(); void save(Usuario usuario); void update(Usuario usuario); void delete(int id);}
public class UsuarioDAOImpl implements UsuarioDAO { private Connection conn;
@Override public Usuario findById(int id) { String sql = "SELECT * FROM usuarios WHERE id = ?"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, id); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return mapResultSetToUsuario(rs); } } catch (SQLException e) { throw new DAOException(e); } return null; }
private Usuario mapResultSetToUsuario(ResultSet rs) throws SQLException { return new Usuario( rs.getInt("id"), rs.getString("nombre"), rs.getInt("edad") ); }}Cómo responder en entrevista:
“El patrón DAO separa la lógica de acceso a datos de la lógica de negocio. La interfaz define el contrato CRUD y cada implementación puede usar distinta tecnología (JDBC, JPA, MongoDB). Facilita el testing porque puedo mockear el DAO. En Spring Boot suelo usar
JpaRepositoryque implementa este patrón con generics.”