{
 "subtemas": [
  "Sintaxis y tipos",
  "POO",
  "String y texto",
  "Colecciones",
  "Genéricos",
  "Excepciones",
  "Streams y lambdas",
  "Concurrencia",
  "JVM y rendimiento",
  "Java moderno",
  "I/O y serialización",
  "Fechas y hora",
  "Snippets: ¿qué imprime?",
  "JDBC y localización"
 ],
 "sub_old": {
  "J01": "JVM y rendimiento",
  "J02": "JVM y rendimiento",
  "J03": "JVM y rendimiento",
  "J04": "String y texto",
  "J05": "POO",
  "J06": "Colecciones",
  "J07": "Colecciones",
  "J08": "Colecciones",
  "J09": "POO",
  "J10": "Excepciones",
  "J11": "Excepciones",
  "J12": "Sintaxis y tipos",
  "J13": "Streams y lambdas",
  "J14": "Streams y lambdas",
  "J15": "Concurrencia",
  "J16": "Concurrencia",
  "J17": "Concurrencia",
  "J18": "Java moderno",
  "J19": "POO",
  "J20": "POO"
 },
 "tarjetas": [
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Cuántos tipos de datos primitivos hay y cuáles son?",
   "a": "8 primitivos: byte (8 bits), short (16), int (32), long (64), float (32), double (64), char (16, Unicode) y boolean.\nLos demás tipos son objetos (clases, arrays, interfaces).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Primitivos vs wrappers (Integer, Double...)",
   "a": "Primitivos: valor puro, rápido, no null, no van en colecciones.\nWrappers: objetos inmutables, permiten null, necesarios en genéricos/colecciones.\nAutoboxing convierte automáticamente en ambas direcciones (con costo de rendimiento en loops).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es el Integer cache y cuándo sorprende?",
   "a": "Integer.valueOf() cachea de -128 a 127: dos autoboxed en ese rango son == el mismo objeto; fuera de él, objetos distintos.\nPor eso Integer a=127, b=127 → a==b es true, pero con 128 es false.\nRegla: en wrappers SIEMPRE usar equals(), nunca ==.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es el autoboxing y su trampa clásica?",
   "a": "Conversión automática primitiva ↔ wrapper.\nTrampa 1: un wrapper null al desempaquetar lanza NullPointerException.\nTrampa 2: en loops grandes genera objetos (perf).\nTrampa 3: comparaciones mixtas: int == Integer desempaqueta (NPE si null).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Java pasa parámetros por valor o por referencia?",
   "a": "SIEMPRE por valor.\nPara objetos, el valor copiado es la referencia: el método puede mutar el objeto apuntado, pero reasignar el parámetro (param = otro) NO afecta al llamador.\nPrimitivos: copia directa, sin efecto externo.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Overflow aritmético en int: ¿qué pasa?",
   "a": "Los enteros hacen wrap-around silencioso (sin excepción): Integer.MAX_VALUE + 1 → Integer.MIN_VALUE.\nSoluciones: usar long, Math.addExact/subtractExact (lanzan ArithmeticException), o BigInteger.\nClásico bug: promedio (a+b)/2 → mejor a + (b-a)/2.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Casting entre tipos numéricos: implícito vs explícito",
   "a": "Implícito (widening): sin pérdida, byte→short→int→long→float→double (int→float puede perder precisión en números grandes).\nExplícito (narrowing): obligatorio y trunca: int x=(int)3.99 → 3; byte b=(byte)130 → -126 (wrap).\nchar participa como numérico (0..65535).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es var (Java 10+) y qué NO es?",
   "a": "Inferencia de tipo local: var lista = new ArrayList<String>(); el tipo se fija en compilación (sigue siendo estático).\nNO es tipo dinámico, NO aplica a campos, parámetros ni retornos, NO con null solo, NO sin inicializador.\nÚsalo cuando el tipo es evidente a la derecha.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Diferencia entre ++i e i++",
   "a": "++i (pre-incremento): incrementa y devuelve el valor nuevo.\ni++ (post-incremento): devuelve el valor viejo y luego incrementa.\nEn x = i++ se asigna el valor anterior. En expresiones complejas mezclarlos dificulta leer el código.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué hace el operador ternario y su trampa con autoboxing?",
   "a": "condicion ? valorSi : valorNo.\nTrampa: si un brazo es primitivo y otro wrapper, el resultado se unifica (puede desempaquetar y lanzar NPE con null):\nInteger x = cond ? 1 : null; → ok\nInteger y = cond ? 1 : objetoIntegerNull; → NPE al evaluar.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "switch clásico: ¿qué pasa sin break?",
   "a": "Cae al siguiente caso (fall-through) y ejecuta todo lo demás hasta un break o el final.\nEl switch de expresión (Java 14+) con flecha (case X ->) NO cae: cada rama es independiente y requiere exhaustividad con enums/sealed.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "for vs while vs do-while vs for-each",
   "a": "for: núcleo conocido de iteraciones.\nwhile: repite mientras la condición sea true (puede no ejecutarse nunca).\ndo-while: ejecuta al menos una vez.\nfor-each (for tipo x : colección): recorre iterables sin índice; no permite modificar la estructura ni saltar elementos fácilmente.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Arrays en Java: características clave",
   "a": "Tamaño fijo, longitud con .length (sin paréntesis), índices 0..n-1, covariantes (String[] es Object[] → ArrayStoreException posible), inicialización: int[] a = {1,2,3};\nArrays.copyOf, Arrays.sort, Arrays.asList (vista de tamaño fijo).\nFuera de rango → ArrayIndexOutOfBoundsException.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué significa que un literal sea 1_000_000 o 0b1010?",
   "a": "Subrayados en literales numéricos solo separan dígitos (legibilidad), desde Java 7.\nPrefijos: 0x hexadecimal, 0b binario, 0 octal (¡trampa!), sufijos L long, f float, d double.\n'\\u0041' es escape Unicode para 'A'.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "BigDecimal: ¿cuándo y por qué?",
   "a": "Para dinero y precisión decimal exacta: double/float usan binario (0.1+0.2 != 0.3).\nCrear SIEMPRE con String: new BigDecimal(\"0.1\"), nunca new BigDecimal(0.1) (arrastra el error binario).\nComparar con compareTo, no equals (equals considera escala: 1.0 != 1.00).\nEspecificar scale y RoundingMode en divide().",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Diferencia entre variable local, de instancia y estática?",
   "a": "Local: dentro de un método, vive durante la llamada, DEBE inicializarse antes de usarse (no hay default).\nDe instancia: por objeto, default según tipo (0, false, null).\nEstática: por clase, compartida por todas las instancias, se inicializa al cargar la clase.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es 'effectively final' y por qué importa en lambdas?",
   "a": "Una variable local que no cambia después de inicializarse.\nLas lambdas y clases anónimas solo pueden capturar variables locales effectively final: se copian por valor al crear la lambda.\nSolución para acumular: usar un contenedor mutable (array de 1, AtomicInteger) o mejor refactorizar con reduce/collect.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Orden de inicialización de una clase",
   "a": "1) static: campos y bloques estáticos, en orden de aparición (una vez, al cargar).\n2) Por cada instancia: campos de instancia y bloques de instancia en orden, luego el constructor (que llama implícitamente a super() primero).\nClásico de examen: el constructor de la clase padre corre ANTES que el cuerpo del hijo, pero los campos con inicializador del padre antes de su constructor.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es instanceof y el pattern matching (Java 16+)?",
   "a": "instanceof verifica el tipo en runtime (null → siempre false).\nPattern matching: if (obj instanceof String s) { usar s } — hace el cast y declara la variable en una línea, sin repetir código.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Cómo funciona el paso de String en un switch?",
   "a": "Desde Java 7. Compara usando equals (case-sensitive), no ==.\nNull en el switch → NullPointerException (verificar antes).\nCon enum, los case van SIN calificar el nombre (case LUNES, no case Dia.LUNES).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es NaN y su trampa en comparaciones?",
   "a": "Double.NaN es 'no es un número' (0/0.0).\nNaN == NaN es FALSE; usar Double.isNaN() para detectarlo.\nNaN en ordenamientos/comparaciones puede romper contratos de Comparator (Double.compare sí lo maneja).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "División entera vs decimal",
   "a": "int/int trunca: 7/2 → 3 (no redondea, hacia cero).\n% es el residuo: 7%2 → 1 (funciona con negativos con signo del dividendo).\nPara decimal: que un operando sea double (7/2.0 → 3.5).\nDivisión entera por cero → ArithmeticException; con doubles → Infinity o NaN.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué son los wrappers inmutables y cuáles tienen métodos útiles?",
   "a": "Integer, Long, etc. son inmutables: las operaciones crean nuevos objetos.\nÚtiles: Integer.parseInt/valueOf, MAX_VALUE/MIN_VALUE, compare, Integer.parseInt(s, radix), Integer.toBinaryString, Character.isDigit/isLetter, Double.parseDouble.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "char vs String: diferencias clave",
   "a": "char es un primitivo UTF-16 de 16 bits con comillas simples ('A'); String es objeto inmutable con comillas dobles.\nchar + int hace aritmética ('A'+1 → 66); char + String concatena.\nEmojis y algunos Unicode ocupan 2 chars (surrogate pairs) → cuidado con length().",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Qué es el alcance (scope) de una variable?",
   "a": "Bloque: entre { } donde se declara (locales).\nCampo: toda la clase (instancia/estática).\nParámetro: todo el método.\nUna variable local puede ocultar un campo (this.x para desambiguar).\nLos bloques internos NO pueden redeclarar una local del bloque externo.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "¿Cuándo usar long vs int vs double para dinero/tiempo?",
   "a": "Dinero: NUNCA double → BigDecimal o long de centavos.\nTiempo: epoch millis o nanos no caben en int (2038/overflow) → long.\nContadores pequeños y índices: int.\nIdentificadores grandes (IDs de DB): long.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "Sintaxis y tipos",
   "q": "Método main: firma válida y sobrecarga",
   "a": "public static void main(String[] args) — también varargs String... args.\nJVM solo invoca esa firma exacta; puedes SOBRECARGAR main pero la otra no se ejecuta sola.\nargs nunca es null (puede ser vacío), no incluye el nombre del programa.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Cuáles son los 4 pilares de la POO y qué significa cada uno?",
   "a": "Encapsulación: ocultar estado interno (campos private + métodos de acceso) y exponer comportamiento.\nAbstracción: modelar lo esencial, exponiendo QUÉ hace, no CÓMO (interfaces/abstract).\nHerencia: reutilización is-a de código y tipo (extends).\nPolimorfismo: un mismo mensaje, comportamientos según el tipo real (overriding + dynamic dispatch).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Composición vs herencia: ¿cuándo cada una?",
   "a": "Regla: favorece composición.\nHerencia solo con relación is-a REAL y jerarquías estables (LSP garantizado); reutiliza y sustituye tipos.\nComposición (has-a): delega, flexible, fácil de testear, evita acoplarse a detalles del padre.\nHerencia rompe encapsulación: los cambios del padre afectan a los hijos.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es el dynamic dispatch (enlace dinámico)?",
   "a": "En runtime se invoca el método del TIPO REAL del objeto, no del tipo de la referencia:\nAnimal a = new Perro(); a.hablar(); → ejecuta Perro.hablar().\nSolo aplica a métodos de instancia (no static, no campos, no private/final).\nLos CAMPOS no son polimórficos: se resuelven por el tipo de la referencia (field hiding).",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Constructores: reglas esenciales",
   "a": "Mismo nombre que la clase, sin tipo de retorno.\nSi no defines ninguno, hay uno default sin args (desaparece al definir uno con args).\nthis(...) llama a otro constructor (debe ser la primera línea); super(...) al del padre (también primera línea, implícito si no se escribe).\nConstructores no se heredan ni se sobrescriben (solo se sobrecargan).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "this y super: usos",
   "a": "this: referencia al objeto actual — desambiguar campos (this.x = x), llamar otro constructor (this(...)), pasar el objeto actual.\nsuper: acceder al miembro del padre oculto (super.metodo()) o llamar su constructor (super(...)).\nEn métodos static NO existe ni this ni super.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Modificadores de acceso en Java",
   "a": "private: solo la clase.\n(package-private, sin modificador): solo el mismo paquete.\nprotected: paquete + subclases (aunque estén en otro paquete).\npublic: todos.\nTop-level: solo public o package-private.\nClases deben ser lo más cerradas posible.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Puede una clase abstracta no tener métodos abstractos? ¿Y una interfaz tener estado?",
   "a": "Sí: una clase abstracta puede no tener métodos abstractos (solo evita instanciarse).\nUna interfaz NO puede tener estado (campos de instancia); sus 'campos' son public static final constantes.\nAmbas pueden tener métodos default/static (interfaz) o concretos (abstracta).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Métodos default y static en interfaces (Java 8+)",
   "a": "default: implementación en la interfaz, para evolución de APIs y mixins; la clase puede sobrescribirlo; en conflicto de diamante la clase DEBE resolverlo: InterfazA.super.metodo().\nstatic: método de la interfaz (Interfaz.metodo()), no se hereda.\nprivate (Java 9+): helpers internos compartidos entre defaults.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es una clase inmutable y cómo se escribe una?",
   "a": "Su estado no cambia tras construirse.\nReceta: clase final, campos final private, sin setters, constructor que copia defensivamente colecciones/arrays mutables, getters que devuelven copias de lo mutable, no exponer this durante construcción.\nBeneficios: thread-safe gratis, cacheable, seguro como clave de HashMap.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Patrón Builder: ¿cuándo vale la pena?",
   "a": "Cuando un constructor tendría muchos parámetros (especialmente opcionales del mismo tipo → errores de orden) o pasos opcionales.\nnew Pizza.Builder(\"m\").queso(true).aceitunas().build();\nLegible, inmutable al final, validación centralizada en build().\nAlternativa moderna: records + builder propio o withers.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Clases anidadas: static nested vs inner vs local vs anónima",
   "a": "static nested: no necesita instancia externa (no guarda referencia al exterior).\ninner (no static): requiere instancia externa, accede a sus campos (Outer.this); puede causar memory leaks (actividad Android).\nlocal: dentro de un método.\nanónima: sin nombre, se implementa al vuelo (hoy en día suele reemplazarse por lambdas si es una interfaz funcional).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué métodos define Object y cuáles conviene sobrescribir?",
   "a": "equals, hashCode, toString (casi siempre), clone (raro, mejor copias), getClass, notify/notifyAll/wait (final, concurrencia), finalize (deprecado).\nRegla de oro: equals y hashCode SIEMPRE juntos y consistentes.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Contrato correcto de equals()",
   "a": "Reflexivo: x.equals(x) true.\nSimétrico: x.equals(y) → y.equals(x).\nTransitivo: x=y, y=z → x=z.\nConsistente: mismo resultado en llamadas repetidas.\nx.equals(null) → false (nunca NPE).\nReceta: instanceof o getClass, comparar campos relevantes, usar Objects.equals para null-safe.",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "POO",
   "q": "Contrato de hashCode() y qué pasa si lo rompes",
   "a": "Consistente entre llamadas; iguales por equals → mismo hashCode (obligatorio); desiguales → IDEALMENTE distinto (colisión permitida).\nSi lo rompes: HashMap/HashSet almacenan en buckets distintos y no encuentran objetos 'iguales' → contains() false con un objeto que debería estar.\nhashCode debe usar los MISMOS campos que equals.",
   "nivel": "basico",
   "clave": true
  },
  {
   "sub": "POO",
   "q": "toString(): buenas prácticas",
   "a": "Generado por IDE/records para depurar rápido.\nIncluir campos clave; NO incluir secretos ni datos personales (va a logs).\n getClass().getSimpleName() + \"(a=\" + a + \")\".\nEvitar llamar métodos que disparan lazy loading (StackOverflow/exception en JPA).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Enums: capacidades que casi nadie usa",
   "a": "Constructor private, campos y métodos, puede implementar interfaces, abstract methods por constante (case por valor), valueOf/values(), ordinal (¡evita persistirlo!), compareTo por orden de declaración, EnumSet/EnumMap (rápidos).\nEnum con constructor: enum Planeta { TIERRA(5.97){...} } con campos y comportamiento por constante.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Clases sealed (Java 17+): ¿para qué sirven?",
   "a": "Restringen QUIÉN puede heredar/implementar: public sealed interface Forma permits Circulo, Cuadrado.\nLos permits deben ser final, sealed o non-sealed.\nHabilitan switch con pattern matching exhaustivo (el compilador sabe todas las subclases).\nModela jerarquías cerradas de dominio con seguridad.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Records vs clases normales: ¿cuándo cada uno?",
   "a": "Record: dato inmutable transparente — genera constructor canónico, accesores sin 'get', equals/hashCode/toString; NO tiene setters, no hereda.\nClase: cuando necesitas mutabilidad, identidad con estado variable, herencia, o validación compleja post-construcción.\nRecords perfectos para DTOs, valores, resultados de cálculo y claves compuestas de Map.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Comparable vs Comparator",
   "a": "Comparable (compareTo): orden NATURAL, lo implementa la propia clase (String, Integer).\nComparator: órdenes EXTERNOS y múltiples: Comparator.comparing(User::getNombre).thenComparing(User::getEdad).reversed().\nContrato: sgn(compare(x,y)) == -sgn(compare(y,x)), consistente con equals idealmente; romperlo rompe TreeSet/TreeMap.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es el patrón Método Fábrica estático (effective Java item 1) y por qué preferirlo a constructores?",
   "a": "Métodos static que devuelven instancias: List.of(), Integer.valueOf(), Optional.of().\nVentajas: tienen NOMBRE, pueden cachear, devolver subtipos, no requieren crear objeto nuevo.\nConvenciones: of, ofNullable, valueOf, from, newInstance.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Field hiding vs overriding: diferencia sutil pero de examen",
   "a": "Campos y métodos static se resuelven por el TIPO DE LA REFERENCIA (hiding, en compilación).\nMétodos de instancia se resuelven por el TIPO REAL del objeto (overriding, en runtime).\nPadre p = new Hijo(); p.x da el campo del Padre; p.metodo() ejecuta el del Hijo.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Upcasting y downcasting",
   "a": "Upcasting (hijo→padre): implícito y seguro, pierde acceso a miembros del hijo.\nDowncasting (padre→hijo): cast explícito, lanza ClassCastException si el tipo real no coincide; verificar con instanceof antes.\nDowncasts frecuentes = diseño gritando por polimorfismo o genéricos.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es la covarianza de arrays y su riesgo?",
   "a": "String[] es subtipo de Object[] (arrays covariantes en runtime).\nRiesgo: Object[] arr = new String[1]; arr[0] = 42 compila pero lanza ArrayStoreException en runtime.\nLas colecciones NO son covariantes (List<String> no es List<Object>) — por eso existen los wildcards.",
   "nivel": "avanzado",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Cuándo marcar una clase/método final?",
   "a": "Clase final: no heredable (String, Integer) — inmutabilidad y seguridad, diseños cerrados.\nMétodo final: no sobrescribible — proteger invariantes.\nCosto: pierde flexibilidad/testing con mocks de herencia. Diseña final por defecto en clases de valor; abierto solo donde hay extensión real pensada.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Herencia de constructores y llamada implícita a super()",
   "a": "Los constructores NO se heredan.\nTodo constructor empieza (implícita o explícitamente) con super() — si el padre NO tiene constructor sin args, el hijo DEBE llamar super(args) explícitamente o no compila.\nEl bloque del padre corre antes que el del hijo.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es una interfaz funcional?",
   "a": "Interfaz con EXACTAMENTE un método abstracto (SAM); puede tener defaults/statics.\nAnotación @FunctionalInterface (opcional, la valida el compilador).\nDestino de lambdas y referencias de método.\nCore en java.util.function: Function<T,R>, Predicate<T>, Consumer<T>, Supplier<T>, BiFunction, UnaryOperator.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Diferencia entre abstracción e encapsulación (pregunta trampa)",
   "a": "Abstracción: decidir QUÉ expone el modelo (el contrato, el 'qué') — nivel de diseño.\nEncapsulación: OCULTAR el estado y proteger invariantes (private + métodos) — mecanismo del lenguaje.\nSe puede encapsular sin abstraer bien (getters de todo = encapsulación rota).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "clone() y por qué casi siempre se evita",
   "a": "Object.clone es protected, requiere implementar Cloneable (que no tiene métodos) y hacer super.clone(); con campos mutables hay que hacer copia profunda a mano.\nAlternativas: constructor de copia (new Punto(Punto otro)), método estático copiaDe(), o record (inmutable no necesita clonar).\nEffective Java: prefiere factories de copia.",
   "nivel": "intermedio",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es el acoplamiento y la cohesión? ¿Cómo se relacionan con buen diseño?",
   "a": "Cohesión: una clase con UNA responsabilidad clara (alta cohesión = bien).\nAcoplamiento: cuánto depende una clase de otras (bajo acoplamiento = bien).\nObjetivo: alta cohesión + bajo acoplamiento → fácil de cambiar, testear y reusar.\nHerramientas: interfaces, inyección de dependencias, módulos.",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "Sobrecarga y resolución: ¿qué método se elige?",
   "a": "El compilador elige el método por el tipo ESTÁTICO de los argumentos, con la conversión más específica posible (primitivo exacto > widening > autoboxing > varargs).\nTrampa: sum(5) con sum(int) y sum(long): elige int.\nCon null ambiguo entre sobrecargas → error de compilación (hay que hacer cast).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Se puede sobrecargar/sobrescribir main o un constructor?",
   "a": "main: se puede SOBRECARGAR (otras firmas) pero la JVM solo ejecuta public static void main(String[]).\nConstructores: solo SOBRECARGA (entre ellos con this(...)); no existe overriding de constructores porque no se heredan.\nUn método static NO se sobrescribe (se oculta, hiding).",
   "nivel": "basico",
   "clave": false
  },
  {
   "sub": "POO",
   "q": "¿Qué es y para qué sirve una clase de utilidades (utility class)?",
   "a": "Clase final con constructor private (no instanciable) y solo métodos static: Collections, Arrays, StringUtils.\nÚtil para funciones puras sin estado.\nCrítica: dificulta mocking y polimorfismo; en código testeable se prefieren componentes inyectados con interfaz.",
   "nivel": "basico",
   "clave": false
  }
 ]
}