{
 "subtemas": [],
 "tarjetas": [
  {
   "sub": "Excepciones",
   "q": "Jerarquía completa de Throwable",
   "a": "Throwable → { Error, Exception }.\nError: JVM irrecuperable (OutOfMemoryError, StackOverflowError) — no capturar.\nException → RuntimeException (unchecked: NPE, IllegalArgument, IndexOutOfBounds) y las demás (checked: IOException, SQLException, InterruptedException).\nCustom: extender Exception (checked) o RuntimeException según semántica.",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "Excepciones",
   "q": "¿Cuándo crear una excepción custom y con qué firma?",
   "a": "Cuando el error tiene semántica de dominio (SaldoInsuficienteException).\nExtender RuntimeException para no infectar firmas (regla moderna) o Exception si el llamador DEBE manejarla.\nConstructor estándar: (String mensaje), (String mensaje, Throwable causa) — SIEMPRE preservar la causa para no perder el stack original.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "Orden de catch y multi-catch",
   "a": "Los catch van de MÁS específico a MÁS general; catch de Exception antes que IOException no compila (bloque inalcanzable).\nMulti-catch (Java 7+): catch (IOException | SQLException e) — e es implícitamente final; no pueden ser padre/hijo entre sí.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "¿Cuándo NO se ejecuta el finally?",
   "a": "Casi nunca: ni con return ni con excepción se salta.\nSolo se pierde si: System.exit(), crash de la JVM, hilo daemon interrumpido al cerrar la JVM, o loop infinito dentro del try.\nTrampa legada: un return dentro del finally SILNCIOSAMENTE descarta la excepción o el return del try — prohibido.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "Excepciones suprimidas (suppressed)",
   "a": "En try-with-resources, si el try lanza X y el close() del recurso lanza Y: X se propaga y Y queda adjuntada.\nRecuperarlas: ex.getSuppressed() → Throwable[].\nNo aplica al finally clásico (allí la excepción del finally REEMPLAZA a la original — se pierde).",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "throw vs throws vs Throwable",
   "a": "throw: sentencia que LANZA una instancia (throw new IllegalArgumentException(\"...\")).\nthrows: en la firma, DECLARA que el método puede lanzar ciertas checked (puede listar unchecked pero es ruido).\nThrowable: la superclase (solo se puede lanzar Throwable y subclases).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "Anti-patrones clásicos de manejo de excepciones",
   "a": "1) catch (Exception e) {} — tragarse el error (logging y rethrow o manejar).\n2) catch(Exception e){ throw new RuntimeException(e.getMessage()) } — perder la causa (usar new RuntimeException(msg, e)).\n3) Usar excepciones para control de flujo (costosas: llenan stack trace).\n4) catch Throwable/Error.\n5) Mensajes sin contexto ('error').",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "¿Lanzar excepciones es caro? ¿Por qué?",
   "a": "Sí: se construye el stack trace (recorre la pila) en el constructor, aunque nadie lea el stack.\nEn hot paths de validación frecuente (parsing masivo) pesa.\nAlternativa: resultados tipo Optional/Either o validación previa.\nPara errores REALES, el costo es irrelevante frente a la claridad.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "InterruptedException: qué significa y cómo tratarla",
   "a": "Se lanza en sleep/wait/join cuando el hilo es interrumpido (interrupt()).\nEs CHECKED: fuerza a decidir.\nSi no la manejas: restaurar la interrupción (Thread.currentThread().interrupt()) y salir, para no 'tragarte' la señal de cancelación.\nNunca catch vacío.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "try-with-resources: reglas completas",
   "a": "Recursos: objetos AutoCloseable, separados por ';' (se cierran en orden INVERSO).\nLa variable es effectively final; se pueden declarar fuera y usar 'final var' (Java 9+).\nFunciona también con try(recurso) sin catch.\nPatrón: try (var conn = ...; var st = conn.createStatement()) {...}",
   "nivel": "intermedio",
   "clave": true
  },
  {
   "sub": "Excepciones",
   "q": "Rethrow y pérdida de stack trace",
   "a": "throw e; re-lanza la MISMA excepción conservando stack original.\nthrow new MiExcepcion(msg) sin causa: pierdes la historia → SIEMPRE wrap con causa: new MiExcepcion(\"contexto\", e).\nfillInStackTrace()/StackWalker para casos especiales.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "assert: qué es y por qué no se usa en producción",
   "a": "assert cond : \"mensaje\" — verificación de invariantes de desarrollo.\nSe evalúa SOLO si la JVM corre con -ea (enableassertions); por defecto están DESACTIVADOS → en producción no protegen nada.\nPara validación real: IllegalArgumentException/Objects.requireNonNull (siempre activos).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Excepciones",
   "q": "Objects utility para validar argumentos temprano",
   "a": "Objects.requireNonNull(x, \"no puede ser null\") → NPE con mensaje claro.\nObjects.requireNonNullElse(x, default).\nrequireNonNull dentro de constructores/setters = fail-fast en el punto correcto (no un NPE críptico 10 líneas después).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Sintaxis de lambda: todas las variantes",
   "a": "(int x) -> x*1, (x) -> x*1, x -> x*1, () -> 42, (a, b) -> { int s = a+b; return s; }.\nTipo inferido del contexto (interfaz funcional destino).\nUna expresión = return implícito; con {} se necesita return explícito.\nVariables capturadas: effectively final.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Las 4 interfaces funcionales core y para qué sirven",
   "a": "Function<T,R>: T→R (map). Predicate<T>: T→boolean (filter/test). Consumer<T>: T→void (efecto, forEach/accept). Supplier<T>: ()→T (produce, get).\nVariantes: BiFunction, BiPredicate, UnaryOperator (T→T), BinaryOperator (T,T→T), especializadas IntPredicate, ToIntFunction (evitan boxing).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Method references: las 4 formas",
   "a": "1) Estático: Math::abs\n2) Instancia concreta: System.out::println\n3) Instancia del primer parámetro: String::toUpperCase (equivale s -> s.toUpperCase())\n4) Constructor: ArrayList::new\nPasa cualquier firma compatible con la interfaz funcional.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "¿Qué es un Stream y en qué se diferencia de una colección?",
   "a": "Secuencia de elementos que NO almacena datos: procesa una fuente (colección, array, generador) de forma declarativa, perezosa y (opcionalmente) paralela.\nColección = estructura de datos; Stream = pipeline de operaciones.\nUn stream se consume UNA vez; reutilizarlo lanza IllegalStateException.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Operaciones intermedias vs terminales y la pereza (lazy)",
   "a": "Intermedias (devuelven Stream, perezosas): filter, map, flatMap, sorted, distinct, limit, skip, peek, takeWhile/dropWhile.\nTerminales (disparan el trabajo): collect, forEach, reduce, count, anyMatch/allMatch/noneMatch, findFirst/findAny, toArray, min/max.\nSin terminal, NADA se ejecuta (fuente clásica de 'mi stream no hace nada').",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "map vs flatMap: la diferencia que siempre preguntan",
   "a": "map: 1 elemento → 1 elemento transformado (Stream<R>).\nflatMap: 1 elemento → MUCHOS elementos aplanados: lista de listas → lista plana (Stream<R> desde Streams internos).\nEjemplo: pedidos.stream().flatMap(p -> p.getItems().stream()) = todos los ítems.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "reduce: las 3 formas y sus riesgos",
   "a": "1) Optional<T> reduce(accumBinaryOp)\n2) T reduce(identity, accum)\n3) <U> U reduce(identity, accum, combiner) (paralelo).\nRiesgos: identity NO neutro rompe paralelismo (usar 0 para suma, no -1); accum asociativo para paralelo.\nPara colecciones mejor Collectors.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Collectors que hay que dominar",
   "a": "toList/toSet/toCollection, joining(delimitador), counting, summingInt/averagingInt, summarizingInt (stats), groupingBy (y con downstream), partitioningBy (boolean), toMap(keyFn, valFn[, mergeFn, mapSupplier]), mapping, filtering, collectingAndThen, teeing (dos recolectores + merge).\ntoMap SIN mergeFn lanza IllegalStateException con claves duplicadas.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "groupingBy: los niveles que salen en exámenes",
   "a": "1 nivel: groupingBy(Persona::getCiudad) → Map<Ciudad, List<Persona>>.\nCon downstream: groupingBy(Persona::getCiudad, counting()) → Map<Ciudad, Long>.\nCon mapping: groupingBy(c, mapping(Persona::getNombre, toList())).\nAnidado: groupingBy(a, groupingBy(b, counting())) → conteo por dos dimensiones.\npartitioningBy es el caso booleano → Map<Boolean, List>.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "peek(), parallelStream() y sus trampas",
   "a": "peek: pensado para DEBUG (ver elementos que pasan); NO garantiza ejecución salvo terminal, y abusar de él para mutar es frágil.\nparallelStream: usa ForkJoinPool.commonPool — SOLO para fuentes fácilmente dividibles y operaciones SIN estado, asociativas, sin efectos secundarios; NO para I/O; puede degradar (compartido en toda la JVM).\nfindAny/forEach en paralelo no preservan orden; usar forEachOrdered.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Streams de primitivas: IntStream y amigos",
   "a": "IntStream/LongStream/DoubleStream evitan boxing.\nCrear: IntStream.range(0,n) / rangeClosed, lista.stream().mapToInt(String::length).\nExtras: sum(), average(), summaryStatistics(), boxed() para volver a Stream<Integer>.\nmap vs mapToInt: mapToObj, mapToDouble para cruzar.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Short-circuit: anyMatch, findFirst y streams infinitos",
   "a": "anyMatch/allMatch/noneMatch, findFirst/findAny, limit — cortan el procesamiento cuando ya tienen respuesta.\nPermite streams infinitos: Stream.iterate(1, i -> i*2).limit(10), Stream.generate(supplier).\nJava 9+: iterate con predicado de parada, Stream.ofNullable.\nforEach NO corta (usar takeWhile).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Optional en streams y su uso correcto",
   "a": "findFirst/findAny/min/max/reduce devuelven Optional.\nEncadenar: stream.findFirst().map(String::toUpperCase).orElse(\"-\").\norElse vs orElseGet: orElse EVALÚA SIEMPRE el argumento (cuidado con llamadas costosas); orElseGet es perezoso.\norElseThrow(supplier) para error explícito.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "sorted(), distinct() y sus costos ocultos",
   "a": "sorted: estado con todo el stream en memoria + ordena O(n log n) — no es perezoso de verdad.\ndistinct: mantiene un HashSet interno (necesita equals/hashCode).\nsorted después de un filter reduce datos; Comparator.comparing natural.\nEn paralelo sorted es aún más caro.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "¿Cuándo NO usar streams?",
   "a": "Lógica con múltiples efectos/branches complejos (mejor loop claro).\nMutación de estado externo dentro del pipeline (violación + bugs en paralelo).\nSolo un par de operaciones triviales donde el loop es igual de claro.\nRendimiento crítico con elementos primitivos y acceso indexado (arrays + loops).\nLegibilidad primero.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Lambda vs clase anónima: diferencias finas",
   "a": "Lambda: solo interfaces funcionales, this se refiere a la clase ENVOLVENTE (no a la anónima), no crea una clase nueva por uso (invokeDynamic).\nAnónima: sirve para clases/abstractas, tiene su propio this, puede tener estado/campos.\nPara implementar interfaces funcionales simples, lambda siempre.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Comparator moderno con lambdas",
   "a": "Comparator.comparing(P::getNombre) / .comparingInt(P::getEdad), thenComparing, reversed(), nullsFirst/nullsLast(Comparator.naturalOrder()).\nEn streams: sorted(Comparator.comparing(...)).\nmax/min de stream aceptan comparator directamente.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Streams y lambdas",
   "q": "Collectors.teeing y collectingAndThen (nivel pro)",
   "a": "teeing(f1, f2, merge): combina dos recolecciones sobre el mismo stream — ej. máx y mín juntos, o porcentaje: teeing(counting(), summingInt(...), (n, s) -> 100.0*s/n).\ncollectingAndThen(collector, finisher): transforma el resultado final — ej. toList → List.copyOf, o toSet → TreeSet::new.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "java.time: las clases que hay que conocer (y qué reemplazan)",
   "a": "LocalDate (solo fecha), LocalTime, LocalDateTime, ZonedDateTime (con zona), OffsetDateTime (con offset), Instant (punto en la línea UTC, timestamps), Duration (tiempo/horas), Period (días/meses/años), DateTimeFormatter, ZoneId.\nReemplazan a Date/Calendar (mutables, meses 0-based, zona confusa — legado).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "¿Todas inmutables y thread-safe? ¿Y por qué importa?",
   "a": "Sí: java.time es inmutable — cada operación devuelve NUEVA instancia (fecha.plusDays(1) NO muta).\nComparte instancias entre hilos sin riesgo (Date/Calendar no).\nTrampa clásica: fecha = fecha.plusDays(1); sin reasignar se 'pierde' el cambio.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "Duration vs Period vs Instant",
   "a": "Duration: cantidad de TIEMPO en escala de reloj (horas, minutos, segundos, nanos) — Duration.between(ini, fin).\nPeriod: cantidad en CALENDARIO (años, meses, días) — Period.between(f1, f2).\nInstant: punto exacto en UTC (epoch seconds+nanos) — para timestamps y machine time.\nZonedDateTime para mostrar a humanos con zona.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "DateTimeFormatter: parseo y formato seguro",
   "a": "DateTimeFormatter.ofPattern(\"dd/MM/yyyy HH:mm\") o constantes ISO (ISO_LOCAL_DATE).\nFormato: fecha.format(fmt). Parseo: LocalDate.parse(\"2024-01-31\") (ISO) o LocalDate.parse(s, fmt).\nIMMUTABLE y thread-safe (SimpleDateFormat NO lo era — bug clásico).\nLocales: ofPattern(..., Locale.forLanguageTag(\"es\")).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "Zonas horarias: la regla de oro al guardar fechas",
   "a": "Guardar y transmitir en UTC (Instant) o timestamps; convertir a zona SOLO al mostrar.\nZonedDateTime.now(ZoneId.of(\"America/Lima\")); conversión: zdt.withZoneSameInstant(otraZona).\nNunca hacer aritmética de fechas con Strings ni asumir la zona del servidor.\nOffsetDateTime vs ZonedDateTime: offset fijo vs reglas de zona (DST).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "Operaciones frecuentes con LocalDate",
   "a": "plusDays/plusMonths/plusYears/plus(Period), minusX, withDayOfMonth, atStartOfDay(), atTime(h,m), toLocalDate/toLocalDateTime, isBefore/isAfter/isEqual, until(otro, ChronoUnit.DAYS).\nPrimer/último del mes: with(TemporalAdjusters.firstDayOfMonth()), lastDayOfMonth.\nlengthOfMonth(), isLeapYear().",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "LocalDate vs LocalDateTime vs Instant: ¿cuál guardar para un evento?",
   "a": "Instant (o timestamp UTC en DB): eventos de sistema, auditoría,.createdAt — sin ambigüedad.\nLocalDate: fechas de calendario puras (cumpleaños, vencimientos sin hora).\nLocalDateTime: hora local sin zona (horarios de tienda) — solo si TODOS comparten zona.\nRegla: si cruza zonas o sistemas → Instant/ZonedDateTime.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "¿Cómo migrar código legado con Date y Calendar?",
   "a": "Convertir: date.toInstant() (Java 8+), Instant→Date: Date.from(instant).\nCalendar: cal.toInstant().\nNuevas APIs no aceptan Date: convertir en la frontera del sistema legado y usar java.time por dentro.\nEvitar mezclar en el mismo dominio.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "Comparar y medir intervalos con java.time",
   "a": "isBefore/isAfter/isEqual entre objetos del mismo tipo.\nDuration.between(t1, t2).toMillis() / toMinutes().\nChronoUnit.DAYS.between(f1, f2), MONTHS.between.\nbetween exige tipos compatibles (LocalDate con LocalDate, Instant con Instant).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Fechas y hora",
   "q": "Reloj inyectable (Clock) para código testeable",
   "a": "Clock.systemDefaultZone() en producción; Clock.fixed(instant, zone) en tests.\nInstanciar servicios con Clock y usar Instant.now(clock) / LocalDate.now(clock) → pruebas deterministas de 'hoy', vencimientos y expiraciones sin sleeps.",
   "nivel": "basico",
   "clave": false
  }
 ]
}