{
 "subtemas": [],
 "tarjetas": [
  {
   "sub": "Java moderno",
   "q": "switch de expresión (Java 14+): qué aporta",
   "a": "Devuelve VALOR: int dias = switch(mes) { case ENE, MAR -> 31; default -> 30; }.\nFlecha -> sin fall-through; caso múltiple con comas; yield para bloques: case X -> { ... yield resultado; }.\nExhaustividad: con enums/sealed, cubrir todos los casos reemplaza el default.\nMenos bugs de break olvidado.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Pattern matching para switch (Java 21): ejemplo con sealed",
   "a": "sealed interface Forma permits Circulo, Cuadrado.\nString nombre = switch(forma) { case Circulo c -> \"círculo r=\" + c.radio(); case Cuadrado c when c.lado() > 10 -> \"cuadrado grande\"; default -> \"otro\"; }.\nGuardas con when; el compilador verifica exhaustividad — agregar un subtipo rompe la compilación de todos los switch, que es BUENO.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Record patterns (Java 21): deconstrucción",
   "a": "record Punto(int x, int y) {}\nif (obj instanceof Punto(int x, int y)) { usar x, y } — extrae componentes directamente.\nAnidados: case Linea(Punto(int x1,_), Punto p2) — '_' es unnamed variable (Java 21+).\nElimina getters intermedios en lógica de descomposición.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Variedades de Optional (Java 9-11): or, ifPresentOrElse, stream",
   "a": "or(supplier): Optional alternativo perezoso.\nifPresentOrElse(consumer, runnable): hace una cosa si hay valor, otra si no (adiós if/else con isPresent).\nstream(): convierte Optional<Stream> flat en un stream — ideal en flatMap: repositorio.findById(id).stream()...\nisEmpty() complementa isPresent.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "HttpClient (Java 11+): uso básico",
   "a": "HttpClient client = HttpClient.newHttpClient();\nHttpRequest req = HttpRequest.newBuilder(URI.create(url)).header(\"Accept\",\"application/json\").GET().build();\nHttpResponse<String> resp = client.send(req, BodyHandlers.ofString()); — también sendAsync (CompletableFuture).\nSoporta HTTP/2, timeouts (connectTimeout, request timeout), y reemplaza HttpURLConnection.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Nuevos métodos de colección (Java 8-11) que debes usar",
   "a": "forEach/forEachRemaining, removeIf, replaceAll, computeIfAbsent/compute/merge (Map), getOrDefault, putIfAbsent, List.sort, Iterator.removeRemaining? — y en Java 21: SequencedCollection (getFirst/getLast/reversed() para List/Deque).\nList.of/Set.of/Map.of + ofEntries y Map.entry (Java 9).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "¿Qué es JPMS (Project Jigsaw, Java 9) y cuándo importa?",
   "a": "Sistema de módulos: module-info.java declara requires y exports — encapsulación REAL (paquetes no exportados no son accesibles ni por reflexión por defecto).\nImporta para: JDK modularizado (jlink para runtimes mínimos), bibliotecas fuertemente encapsuladas.\nLa mayoría de apps de negocio NO usan módulos propios, pero conviven con el JDK modular.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Unnamed variables y patterns (_) — Java 21/22",
   "a": "Usa '_' cuando no necesitas la variable: catch (Exception _), for (var _ : lista), pattern Punto(int _, int y).\nExpressa intención: 'esto aquí no importa' y evita nombres throw-away (ignored, unused).\nAdemás unnamed classes y el main compacto (Java 21+ preview → 25 final) para scripts.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Anotaciones: @Retention, @Target y cómo se leen en runtime",
   "a": "@Retention: SOURCE (solo compilador), CLASS (default, no runtime), RUNTIME (reflexión las ve — Spring/JPA usan RUNTIME).\n@Target: dónde aplica (METHOD, FIELD, TYPE, PARAMETER).\nLeerlas: clase.getMethod(\"x\").getAnnotation(MiAnon.class).\nEs el mecanismo que usan los frameworks para configurar sin XML.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Reflection: usos legítimos y costos",
   "a": "Instanciar/invocar en runtime por nombre: Class.forName, getDeclaredConstructor().newInstance(), method.invoke(obj, args) — setAccessible(true) para privados.\nUsos: frameworks (DI, ORMs, serializers), testing de privados (mejor no).\nCostos: lento vs llamada directa, rompe encapsulación, choca con modules/native-image. Cachear Method/Constructor si se usa mucho.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Proxy dinámico: cómo funciona Spring AOP por dentro",
   "a": "Proxy.newProxyInstance(loader, interfaces, handler) crea en runtime una clase que implementa las interfaces y delega al InvocationHandler.\nSpring: si tu bean implementa interfaces usa proxy JDK (por interfaz); si no, CGLIB (subclase generada).\nPor eso: métodos final/static/private no se interceptan y self-invocation no pasa por el proxy.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "Virtual threads + Structured Concurrency: el nuevo modelo",
   "a": "StructuredTaskScope (preview): lanza subtareas virtuales y garantiza que NINGUNA quede huérfana — scope.join() espera; shutdownOnFailure o shutdownOnSuccess para cancelar hermanos.\nCódigo concurrente con la claridad del secuencial: try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { ... scope.join().throwIfFailed(); }",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "JShell y scripts Java: qué permiten hoy",
   "a": "JShell (Java 9+): REPL — probar APIs, calcular, explorar sin proyecto.\nScripts: java script.java ejecuta un .java directo (single-file source), con clases locales/unnamed y main compacto para utilidades rápidas.\nEn contenedores: jshell -c o imagen con JDK (no JRE slim).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Java moderno",
   "q": "¿Qué cambió con las versiones LTS y el ritmo semestral?",
   "a": "Ritmo: feature release cada 6 meses; LTS cada 2 años: 8 (2014), 11 (2018), 17 (2021), 21 (2023), 25 (2025).\n21 trae: virtual threads (final), pattern matching switch, record patterns, sequenced collections.\nEmpresas: saltan de LTS a LTS; features 'preview' se estabilizan en 1-3 releases.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "java.io vs java.nio: la diferencia en una frase",
   "a": "I/O clásico: streams bloqueantes orientados a byte/char.\nNIO: channels + buffers + selectores (I/O no bloqueante) y un FileSystem moderno (Path/Files).\nPara apps comunes: Files/Paths (NIO.2) para operaciones de archivo, streams para contenido.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "Files y Path: el recetario de operaciones de archivo",
   "a": "Path p = Path.of(\"dir\", \"archivo.txt\"); Files.readAllLines(p), readString(p), write(p, bytes), exists, createDirectories, copy/move (REPLACE_EXISTING), delete, walk (stream recursivo), list, size, getLastModifiedTime.\nBuffered: Files.newBufferedReader/Writer (charset explícito!).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "Encoding: por qué especificar SIEMPRE el charset",
   "a": "DefaultCharset depende del SO/locale (Windows-1252, UTF-8...) → acentos rotos entre entornos ('Ã©').\nSIEMPRE: new InputStreamReader(in, StandardCharsets.UTF_8), Files.readString(p, UTF_8), getBytes(UTF_8).\nJava 18+: UTF-8 es el default de la plataforma (JEP 400), pero explícito sigue siendo mejor.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "Lectura de archivos grandes sin reventar memoria",
   "a": "NUNCA readAllLines/readAllBytes para GBs.\nUsar: Files.lines(p) (Stream perezoso, cerrar con try-with-resources), BufferedReader.lines(), o streaming por líneas.\nEscribir con BufferedWriter y flush por lotes.\nPara binario: InputStream.transferTo(out) copia en buffer.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "Serializable: cómo funciona y sus peligros",
   "a": "implements Serializable → ObjectOutputStream serializa el GRAFO completo de objetos (transitivamente).\nPeligros: deserialización de datos no confiables = vulnerabilidad grave (gadget chains); versionado frágil (serialVersionUID); expone representación interna.\nAlternativas modernas: JSON (Jackson), Protobuf/Avro.\nSi lo usas: serialVersionUID fijo + transient para no serializar campos.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "transient y serialVersionUID",
   "a": "transient: campo NO se serializa (contraseñas, conexiones, cachés derivadas) — al deserializar queda null/0.\nserialVersionUID: huella de versión de la clase; si cambia entre serializar y deserializar → InvalidClassException. Fijarlo explícito para controlar compatibilidad.\nwriteObject/readObject custom para control fino.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "Scanner vs BufferedReader para leer entrada",
   "a": "Scanner: parseo cómodo (nextInt, nextLine) pero lento y con trampa nextInt + nextLine (deja el \\n — consumirlo).\nBufferedReader.readLine: rápido, devuelve String, parseas con Integer.parseInt.\nEn challenges/entrada masiva: BufferedReader + StringTokenizer (split manual rápido).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "I/O y serialización",
   "q": "Temp files, cleaning y Files.walk",
   "a": "Files.createTempFile(prefix, suffix) / createTempDirectory — en %TEMP%.\nBorrar: Files.delete en finally, o deleteOnExit() (solo crece la lista; no para largos).\nFiles.walk(ruta) → Stream<Path> recursivo (cerrarlo); filter por extensiones; Files.walkFileTree para control fino (visitor).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "Integer a=127,b=127; Integer c=128,d=128; a==b ? c==d ?",
   "a": "true y false.\nInteger.valueOf cachea [-128, 127] → a y b son el MISMO objeto.\n128 crea objetos distintos → == compara referencias y da false.\nLección: wrappers SIEMPRE equals().",
   "nivel": "intermedio",
   "clave": true
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "String s=\"hola\"; String t=\"ho\"+\"la\"; s==t ? new String(\"hola\")==s ?",
   "a": "true y false.\n\"ho\"+\"la\" es constante plegada en COMPILACIÓN → misma referencia del pool que s.\nnew String(\"hola\") crea un objeto nuevo en el heap → == false (equals sería true).\ns.intern() == new String(\"hola\").intern() → true.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "List<Integer> l = new ArrayList<>(List.of(1,2,3)); l.remove(1); ¿qué queda?",
   "a": "[1, 3] — remove(1) elimina el ÍNDICE 1 (el 2), no el valor 1.\nPara eliminar el valor: remove(Integer.valueOf(1)).\nEl overload elegido depende del tipo ESTÁTICO del argumento: int → índice; Integer → objeto.",
   "nivel": "intermedio",
   "clave": true
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "try { return \"try\"; } finally { System.out.println(\"fin\"); } — ¿orden de salida?",
   "a": "Imprime 'fin' y LUEGO devuelve \"try\": el finally corre ANTES de que el return del try complete la salida del método.\nSi el finally hiciera return \"finally\", ese valor GANA y se silencian excepciones pendientes (mala práctica).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "System.out.println(0.1+0.2 == 0.3); y System.out.println(1/2);",
   "a": "false y 0.\n0.1+0.2 = 0.30000000000000004 en binario IEEE754 → comparar doubles con epsilon o BigDecimal.\n1/2 es división ENTERA (trunca a 0); 1.0/2 sería 0.5.",
   "nivel": "intermedio",
   "clave": true
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "Integer x = null; int y = (condicion) ? x : 0; — ¿qué pasa si condicion es true?",
   "a": "NullPointerException.\nEl ternario con un brazo int y otro Integer fuerza UNBOXING de ambos → desempaqueta el null.\nFix: int y = (cond && x != null) ? x : 0; o evitar mezclar primitivo y wrapper en ternarios.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "char c = 'A'; System.out.println(c + 1); System.out.println(\"\" + c + 1);",
   "a": "66 y A1.\nchar + int = aritmética (promoción a int → 65+1).\nString + char inicia concatenación → \"A1\" (todo lo que sigue se concatena).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "Map<Integer,String> m = new HashMap<>(); m.put(1,\"a\"); m.put(null,\"b\"); ¿lanza algo? ¿y en TreeMap?",
   "a": "HashMap: perfecto, admite UNA clave null (y valores null) — no lanza nada.\nTreeMap.put(null,...) → NullPointerException (comparar null con orden natural rompe el árbol).\nConcurrentHashMap → NullPointerException también.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "static int x=10; static { x=20; } ¿en qué orden se ejecuta con new Clase()?",
   "a": "En orden de aparición del archivo: primero x=10 (inicializador de campo), luego el bloque static x=20.\nTodo el bloque estático corre UNA vez al cargar la clase, ANTES de cualquier instancia y antes de main.\nReordenar líneas cambia el resultado final → pregunta clásica de examen.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "double d = Double.NaN; d == d — ¿true o false?",
   "a": "false.\nNaN es lo único distinto de sí mismo en IEEE754.\nDetectarlo: Double.isNaN(d). Y NaN en un HashSet puede duplicarse (equals falsificado por comparación de bits: Double.valueOf(NaN).equals(NaN) sí es true — inconsistencia deliberada).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "String s = \"a\"; s.concat(\"b\"); System.out.println(s);",
   "a": "\"a\" — String es INMUTABLE: concat devuelve un NUEVO String que se descarta al no asignarlo.\nLo mismo con toUpperCase, replace, trim...\nSolo si haces s = s.concat(\"b\") cambia la variable (a un objeto nuevo).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "List.of(\"a\").add(\"b\") — ¿qué pasa?",
   "a": "UnsupportedOperationException EN RUNTIME.\nList.of crea colecciones INMUTABLES; cualquier mutación lanza excepción.\nSi necesitas mutable: new ArrayList<>(List.of(\"a\")) — constructor copia.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "HashSet<Point> set: agrego p (x=1), luego p.x=2, set.contains(p nuevo con x=2) → ?",
   "a": "false (o comportamiento inconsistente): el objeto MUTÓ su hashCode DESPUÉS de insertarse — quedó en un bucket que ya no le corresponde.\nEl set puede no encontrar ni eliminar el elemento (remove falla silenciosamente).\nLección: campos que participan en hash/equals deben ser inmutables.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Snippets: ¿qué imprime?",
   "q": "System.out.println(10 + 20 + \"\" + 10 + 20);",
   "a": "\"302010\".\nDe izquierda a derecha: 10+20 (aritmética) = 30; al tocar el String \"\" todo lo demás se CONCATENA: 30 + \"10\" + \"20\" → 302010.\nEvalúo de expresión: la aritmética solo ocurre mientras no haya String en la cadena.",
   "nivel": "intermedio",
   "clave": false
  }
 ]
}