Top 10 Preguntas Esenciales de Java
Las 10 Preguntas Más Importantes de Java
Section titled “Las 10 Preguntas Más Importantes de Java”Estas son las preguntas que siempre aparecen en entrevistas técnicas de Java.
1. ¿Cuáles son los pilares de la Programación Orientada a Objetos?
Section titled “1. ¿Cuáles son los pilares de la Programación Orientada a Objetos?”Introducción: Pregunta fundamental en cualquier entrevista de Java. Los 4 pilares son la base de todo el paradigma orientado a objetos.
Definición profesional: Los pilares de POO son los principios fundamentales que definen cómo se estructuran y relacionan los objetos en un sistema: Encapsulamiento, Herencia, Polimorfismo y Abstracción.
Explicación sencilla:
- Encapsulamiento: Ocultar los datos internos y controlar el acceso mediante métodos
- Herencia: Una clase hija reutiliza código de una clase padre
- Polimorfismo: Un mismo método se comporta diferente según el objeto
- Abstracción: Mostrar solo lo esencial, ocultar la complejidad
Funcionamiento básico:
| Pilar | Qué hace | Cómo se implementa |
|---|---|---|
| Encapsulamiento | Protege datos | private + getters/setters |
| Herencia | Reutiliza código | extends / implements |
| Polimorfismo | Múltiples formas | Override de métodos |
| Abstracción | Oculta complejidad | abstract / interface |
Ejemplo práctico:
// ENCAPSULAMIENTO - datos privados, acceso controladopublic class Cuenta { private double saldo;
public double getSaldo() { return saldo; } public void depositar(double monto) { if (monto > 0) saldo += monto; }}
// HERENCIA - reutilizar códigopublic class CuentaAhorro extends Cuenta { private double tasaInteres;}
// POLIMORFISMO - mismo método, diferente comportamientopublic interface Notificador { void enviar(String mensaje);}
public class EmailNotificador implements Notificador { public void enviar(String mensaje) { System.out.println("Email: " + mensaje); }}
public class SMSNotificador implements Notificador { public void enviar(String mensaje) { System.out.println("SMS: " + mensaje); }}
// ABSTRACCIÓN - solo lo esencialpublic abstract class Figura { public abstract double calcularArea();}Casos de uso reales:
- Encapsulamiento: Validar datos antes de asignarlos (ej: edad no negativa)
- Herencia: Clases de empleados que heredan de Persona
- Polimorfismo: Diferentes métodos de pago (tarjeta, PayPal, transferencia)
- Abstracción: Interfaces de repositorio que ocultan si es BD o API
Cómo responder en entrevista:
“Los 4 pilares de POO son Encapsulamiento, Herencia, Polimorfismo y Abstracción. El encapsulamiento protege los datos usando modificadores de acceso. La herencia permite reutilizar código entre clases. El polimorfismo permite que un mismo método tenga diferentes comportamientos. Y la abstracción nos permite modelar solo lo esencial ocultando la complejidad.”
2. ¿Cuál es la diferencia entre == y .equals()?
Section titled “2. ¿Cuál es la diferencia entre == y .equals()?”Introducción: Una de las preguntas más frecuentes. Confundir estos operadores causa bugs difíciles de detectar.
Definición profesional:
== es un operador que compara referencias de memoria (si dos variables apuntan al mismo objeto). .equals() es un método que compara el contenido o valor lógico de los objetos.
Explicación sencilla:
==pregunta: “¿Son el mismo objeto en memoria?”.equals()pregunta: “¿Tienen el mismo valor?”
Funcionamiento básico:
| Operador | Compara | Primitivos | Objetos |
|---|---|---|---|
== | Referencias | Valor directo | Dirección de memoria |
.equals() | Contenido | N/A | Valor lógico |
Sintaxis:
objeto1 == objeto2 // Compara referenciasobjeto1.equals(objeto2) // Compara contenidoEjemplo práctico:
String s1 = new String("Hola");String s2 = new String("Hola");String s3 = s1;
// == compara referenciasSystem.out.println(s1 == s2); // false (objetos diferentes)System.out.println(s1 == s3); // true (misma referencia)
// .equals() compara contenidoSystem.out.println(s1.equals(s2)); // true (mismo valor)
// Caso especial: String PoolString s4 = "Hola";String s5 = "Hola";System.out.println(s4 == s5); // true (String Pool reutiliza)Casos de uso reales:
- Comparar IDs de usuario: usar
.equals() - Verificar si dos variables apuntan al mismo objeto: usar
== - Comparar objetos en colecciones (HashMap, HashSet): siempre
.equals()
Cómo responder en entrevista:
“El operador
==compara referencias, es decir, si dos variables apuntan al mismo objeto en memoria. El método.equals()compara el contenido o valor lógico de los objetos. Para comparar objetos siempre debemos usar.equals(), y es importante sobrescribir tantoequals()comohashCode()en nuestras clases.”
3. ¿Qué es el String Pool?
Section titled “3. ¿Qué es el String Pool?”Introducción:
Concepto clave para entender cómo Java optimiza el manejo de Strings y por qué == a veces funciona con Strings.
Definición profesional: El String Pool es un área especial en el Heap donde la JVM almacena literales de String para reutilizarlos, optimizando memoria al evitar duplicados.
Explicación sencilla:
Cuando escribes "Hola" en tu código, Java guarda ese texto en un “almacén”. Si escribes "Hola" otra vez, Java reutiliza el mismo objeto del almacén en lugar de crear uno nuevo.
Funcionamiento básico:
- Los literales de String (
"texto") van automáticamente al Pool new String()crea objetos FUERA del Poolintern()fuerza a un String a usar el Pool
Ejemplo práctico:
// Literales van al String PoolString s1 = "Java";String s2 = "Java";System.out.println(s1 == s2); // true (misma referencia del Pool)
// new String() crea objeto fuera del PoolString s3 = new String("Java");System.out.println(s1 == s3); // false (diferente referencia)
// intern() fuerza a usar el PoolString s4 = s3.intern();System.out.println(s1 == s4); // true (ahora usa el Pool)Casos de uso reales:
- Optimización de memoria en aplicaciones con muchos Strings repetidos
- Comparaciones rápidas con
==cuando sabemos que son literales - Caché de configuraciones o constantes de texto
Cómo responder en entrevista:
“El String Pool es un área en el Heap donde Java almacena literales de String para reutilizarlos y ahorrar memoria. Cuando usamos literales como
String s = \"Hola\", Java verifica si ya existe en el Pool y reutiliza esa referencia. Por eso dos literales iguales con==dan true. Peronew String()crea objetos fuera del Pool, por eso siempre debemos usar.equals()para comparar Strings.”
4. ¿Cuál es la diferencia entre ArrayList y LinkedList?
Section titled “4. ¿Cuál es la diferencia entre ArrayList y LinkedList?”Introducción: Pregunta clásica sobre estructuras de datos. Demuestra conocimiento de complejidad algorítmica y cuándo usar cada una.
Definición profesional: ArrayList es una implementación de List basada en un array dinámico que ofrece acceso O(1) por índice. LinkedList es una lista doblemente enlazada que ofrece inserción/eliminación O(1) en los extremos.
Explicación sencilla:
- ArrayList: Como un array que crece automáticamente. Rápido para leer, lento para insertar en medio.
- LinkedList: Cadena de nodos conectados. Rápido para insertar/eliminar, lento para buscar.
Funcionamiento básico:
| Operación | ArrayList | LinkedList |
|---|---|---|
| Acceso por índice | O(1) ✅ | O(n) ❌ |
| Inserción al final | O(1)* | O(1) |
| Inserción en medio | O(n) ❌ | O(1) ✅ |
| Búsqueda | O(n) | O(n) |
| Memoria | Menos | Más (nodos) |
*Amortizado
Ejemplo práctico:
import java.util.*;
// ArrayList - mejor para acceso aleatorioList<String> arrayList = new ArrayList<>();arrayList.add("A");arrayList.add("B");String elemento = arrayList.get(0); // O(1) - Muy rápido
// LinkedList - mejor para inserciones/eliminaciones frecuentesLinkedList<String> linkedList = new LinkedList<>();linkedList.addFirst("A"); // O(1)linkedList.addLast("B"); // O(1)linkedList.removeFirst(); // O(1)
// Regla general: usar ArrayList por defectoList<String> lista = new ArrayList<>();Casos de uso reales:
- ArrayList: Lista de productos, resultados de búsqueda, cualquier lectura frecuente
- LinkedList: Implementar colas (Queue), historial de navegación, undo/redo
Cómo responder en entrevista:
“ArrayList usa un array dinámico internamente, lo que permite acceso O(1) por índice pero inserción O(n) en medio. LinkedList usa nodos enlazados, ofreciendo inserción O(1) pero acceso O(n). En la práctica, ArrayList es mejor en la mayoría de casos porque el acceso a memoria es más eficiente. Solo usaría LinkedList si necesito muchas inserciones/eliminaciones al inicio o final de la lista.”
5. ¿Cuál es la diferencia entre Interface y Abstract Class?
Section titled “5. ¿Cuál es la diferencia entre Interface y Abstract Class?”Introducción: Pregunta esencial de diseño orientado a objetos. Demuestra comprensión de cuándo usar cada mecanismo de abstracción.
Definición profesional: Una Interface define un contrato de comportamiento que las clases deben implementar, permitiendo herencia múltiple. Una Abstract Class es una clase parcialmente implementada que sirve como base para subclases, permitiendo compartir código común.
Explicación sencilla:
- Interface: “Qué debe hacer” - Define capacidades (Volador, Serializable)
- Abstract Class: “Qué es” - Define una familia de objetos con código compartido
Funcionamiento básico:
| Aspecto | Interface | Abstract Class |
|---|---|---|
| Herencia | Múltiple (implements) | Simple (extends) |
| Métodos | Abstractos + default | Abstractos + concretos |
| Variables | Solo constantes | Cualquier tipo |
| Constructor | No | Sí |
| Cuándo usar | Definir capacidades | Compartir código base |
Ejemplo práctico:
// INTERFACE - define capacidades/contratopublic interface Volador { void volar();
default void aterrizar() { // Java 8+ System.out.println("Aterrizando..."); }}
public interface Nadador { void nadar();}
// ABSTRACT CLASS - comparte código basepublic abstract class Animal { protected String nombre;
public Animal(String nombre) { this.nombre = nombre; }
public void dormir() { // Método concreto compartido System.out.println(nombre + " duerme"); }
public abstract void hacerSonido(); // Cada animal lo implementa}
// Combinación: hereda de una clase, implementa múltiples interfacespublic class Pato extends Animal implements Volador, Nadador { public Pato(String nombre) { super(nombre); }
public void hacerSonido() { System.out.println("Cuac"); } public void volar() { System.out.println(nombre + " vuela"); } public void nadar() { System.out.println(nombre + " nada"); }}Casos de uso reales:
- Interface:
Comparable,Serializable,Runnable, contratos de servicio - Abstract Class:
HttpServlet,AbstractList, clases base de frameworks
Cómo responder en entrevista:
“La principal diferencia es que una clase puede implementar múltiples interfaces pero solo extender una clase abstracta. Uso interfaces para definir contratos o capacidades que pueden tener objetos no relacionados. Uso clases abstractas cuando tengo una jerarquía de clases que comparten código común. Desde Java 8, las interfaces pueden tener métodos default, pero las clases abstractas siguen siendo útiles cuando necesito estado (variables de instancia) o constructores.”
6. ¿Qué es el manejo de excepciones en Java?
Section titled “6. ¿Qué es el manejo de excepciones en Java?”Introducción: Tema fundamental para escribir código robusto. Los entrevistadores evalúan si conoces la diferencia entre tipos de excepciones y las mejores prácticas.
Definición profesional: El manejo de excepciones es el mecanismo de Java para gestionar errores en tiempo de ejecución de forma controlada, separando el flujo normal del código del manejo de errores.
Explicación sencilla:
- Checked exceptions: El compilador te obliga a manejarlas (ej: leer archivo)
- Unchecked exceptions: Errores de programación, no obligatorio manejar (ej: NullPointer)
- Errors: Problemas graves del sistema, no debes manejar (ej: OutOfMemory)
Funcionamiento básico:
Throwable├── Error (NO manejar: OutOfMemoryError)└── Exception ├── RuntimeException (Unchecked: NullPointerException) └── Checked (IOException, SQLException)Sintaxis:
try { // Código que puede fallar} catch (TipoExcepcion e) { // Manejar error} finally { // Siempre se ejecuta (limpiar recursos)}
// Try-with-resources (Java 7+) - MEJOR PRÁCTICAtry (Recurso r = new Recurso()) { // Recurso se cierra automáticamente}Ejemplo práctico:
// Try-with-resources - forma moderna y recomendadapublic String leerArchivo(String ruta) { try (BufferedReader reader = new BufferedReader(new FileReader(ruta))) { return reader.readLine(); } catch (FileNotFoundException e) { System.out.println("Archivo no existe: " + ruta); return null; } catch (IOException e) { System.out.println("Error de lectura: " + e.getMessage()); return null; } // reader se cierra automáticamente}
// Lanzar excepción personalizadapublic void validarEdad(int edad) { if (edad < 0) { throw new IllegalArgumentException("Edad no puede ser negativa"); }}Casos de uso reales:
- Checked: Operaciones de I/O, conexiones a BD, llamadas a APIs
- Unchecked: Validaciones de negocio, errores de programación
- Try-with-resources: Cualquier recurso que implemente
AutoCloseable
Cómo responder en entrevista:
“Java tiene dos tipos principales de excepciones: Checked, que el compilador obliga a manejar como IOException, y Unchecked que extienden de RuntimeException. La mejor práctica es usar try-with-resources para recursos que necesitan cerrarse, y solo capturar excepciones que realmente podemos manejar. Nunca debemos capturar Exception genérica ni dejar catch blocks vacíos.”
7. ¿Qué son los modificadores de acceso?
Section titled “7. ¿Qué son los modificadores de acceso?”Introducción: Pregunta básica pero importante. Demuestra comprensión del encapsulamiento y diseño de APIs.
Definición profesional: Los modificadores de acceso son palabras clave que controlan la visibilidad de clases, métodos y variables, implementando el principio de encapsulamiento.
Explicación sencilla: Controlan “quién puede ver qué” en tu código:
- public: Todo el mundo
- protected: Familia (mismo paquete + hijos)
- default: Vecinos (mismo paquete)
- private: Solo yo (misma clase)
Funcionamiento básico:
| Modificador | Misma Clase | Mismo Paquete | Subclase | Otro Paquete |
|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
default | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
Ejemplo práctico:
public class Usuario { // private - solo accesible dentro de esta clase private String password;
// default - accesible en el mismo paquete String nombreInterno;
// protected - paquete + subclases protected int id;
// public - accesible desde cualquier lugar public String nombre;
// Getter público para dato privado (encapsulamiento) public String getPassword() { return "****"; // No exponer el real }
public void setPassword(String password) { if (password.length() >= 8) { this.password = password; } }}Casos de uso reales:
- private: Datos sensibles, implementación interna
- public: APIs, métodos de servicio
- protected: Métodos que subclases pueden sobrescribir
- default: Clases auxiliares dentro del mismo paquete
Cómo responder en entrevista:
“Java tiene 4 niveles de acceso: public es visible para todos, protected para el mismo paquete y subclases, default (sin modificador) solo para el mismo paquete, y private solo dentro de la misma clase. La mejor práctica es usar el nivel más restrictivo posible: hacer los campos private y exponer solo lo necesario mediante métodos públicos. Esto implementa el principio de encapsulamiento.”
8. ¿Qué es la palabra clave final?
Section titled “8. ¿Qué es la palabra clave final?”Introducción: Concepto importante para inmutabilidad y seguridad del código. Tiene diferentes significados según dónde se use.
Definición profesional:
final es un modificador que indica que algo no puede ser cambiado: una variable no puede reasignarse, un método no puede sobrescribirse, y una clase no puede heredarse.
Explicación sencilla:
- Variable final: Es una constante, no puedes cambiar su valor
- Método final: Nadie puede sobrescribirlo en subclases
- Clase final: Nadie puede heredar de ella
Funcionamiento básico:
| Contexto | Significado | Ejemplo |
|---|---|---|
| Variable | No reasignable | final int MAX = 100; |
| Método | No sobrescribible | public final void metodo() |
| Clase | No heredable | public final class String |
| Parámetro | No modificable en el método | void foo(final int x) |
Ejemplo práctico:
// VARIABLE FINAL - constantefinal int MAX_USUARIOS = 100;// MAX_USUARIOS = 200; // ❌ Error de compilación
// Objeto final: la referencia no cambia, pero el contenido sí puedefinal List<String> lista = new ArrayList<>();lista.add("elemento"); // ✅ Esto sí se puede// lista = new ArrayList<>(); // ❌ Esto no
// MÉTODO FINAL - no se puede sobrescribirpublic class Padre { public final void metodoCritico() { // Lógica que no debe modificarse }}
// CLASE FINAL - no se puede heredarpublic final class Utilidades { public static int sumar(int a, int b) { return a + b; }}// class MiUtilidad extends Utilidades {} // ❌ ErrorCasos de uso reales:
- Variables: Constantes de configuración, valores inmutables
- Métodos: Lógica crítica que no debe modificarse (seguridad, validaciones)
- Clases:
String,Integer, clases de utilidad, inmutabilidad garantizada
Cómo responder en entrevista:
“La palabra clave
finaltiene tres usos: en variables las hace constantes (no reasignables), en métodos previene que sean sobrescritos en subclases, y en clases previene la herencia. Es importante notar que una variable final de tipo objeto no puede reasignarse, pero el objeto en sí puede modificarse. Por eso para inmutabilidad completa necesitamos que la clase también sea inmutable, como String.”
9. ¿Qué es el método hashCode() y por qué es importante?
Section titled “9. ¿Qué es el método hashCode() y por qué es importante?”Introducción: Pregunta crítica que evalúa comprensión de colecciones hash. Errores aquí causan bugs muy difíciles de detectar.
Definición profesional:
hashCode() es un método que retorna un entero representando al objeto, usado por colecciones hash (HashMap, HashSet) para ubicar objetos eficientemente. Debe ser consistente con equals().
Explicación sencilla:
Es como el “número de casillero” donde se guarda un objeto. Si dos objetos son iguales (equals = true), DEBEN tener el mismo número de casillero.
Funcionamiento básico:
Contrato hashCode-equals:
- Si
a.equals(b)estrue→a.hashCode() == b.hashCode()(OBLIGATORIO) - Si
hashCodees diferente →equalsDEBE serfalse - Si
hashCodees igual →equalspuede sertrueofalse(colisiones)
Ejemplo práctico:
public class Producto { private String codigo; private String nombre;
// SIEMPRE sobrescribir equals Y hashCode juntos @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Producto otro = (Producto) obj; return Objects.equals(codigo, otro.codigo); }
@Override public int hashCode() { return Objects.hash(codigo); // Usar los mismos campos que equals }}
// ¿Por qué importa?Map<Producto, Integer> inventario = new HashMap<>();Producto p1 = new Producto("ABC123", "Laptop");inventario.put(p1, 50);
Producto p2 = new Producto("ABC123", "Laptop");System.out.println(inventario.get(p2)); // 50 ✅ (si hashCode está bien)// Sin hashCode correcto: null ❌Casos de uso reales:
- Usar objetos como claves en HashMap
- Almacenar objetos en HashSet (sin duplicados)
- Cachés basados en hash
- Cualquier estructura que use hashing
Cómo responder en entrevista:
“hashCode() retorna un entero que representa al objeto y es usado por colecciones hash para ubicar objetos eficientemente. El contrato fundamental es: si dos objetos son iguales según equals(), DEBEN tener el mismo hashCode. Por eso siempre que sobrescribimos equals() debemos sobrescribir hashCode() usando los mismos campos. Si no lo hacemos, objetos iguales podrían terminar en diferentes ‘buckets’ y no los encontraríamos en HashMap o HashSet.”
10. ¿Qué es el Garbage Collector?
Section titled “10. ¿Qué es el Garbage Collector?”Introducción: Tema importante para entender gestión de memoria en Java. Demuestra conocimiento de cómo la JVM optimiza recursos.
Definición profesional: El Garbage Collector (GC) es un proceso automático de la JVM que identifica y elimina objetos que ya no son accesibles, liberando memoria del heap sin intervención del programador.
Explicación sencilla: Es el “recolector de basura” de Java. Cuando un objeto ya no tiene ninguna referencia apuntándole, el GC lo detecta y libera esa memoria automáticamente.
Funcionamiento básico:
Un objeto es elegible para GC cuando:
- No hay referencias apuntando a él
- Todas las referencias son
null - El objeto está fuera de scope
Generaciones del Heap:
- Young Generation: Objetos nuevos (la mayoría muere aquí)
- Old Generation: Objetos que sobreviven múltiples GC
- Metaspace: Metadata de clases (Java 8+)
Ejemplo práctico:
public void ejemplo() { // Objeto creado en el heap String s1 = new String("Hola");
// Ahora "Hola" es elegible para GC s1 = null;
// "Mundo" se vuelve elegible cuando s2 apunta a otro objeto String s2 = new String("Mundo"); s2 = new String("Otro"); // "Mundo" → elegible para GC
// System.gc() SUGIERE ejecutar GC (no garantizado) System.gc();}
// Tipos de GC en Java:// - G1 GC: Default desde Java 9, buen balance// - ZGC: Ultra-low latency (Java 11+), pausas < 10ms// - Parallel GC: Alto throughput, pausas más largas// - Serial GC: Para aplicaciones pequeñasCasos de uso reales:
- G1: Aplicaciones web típicas, buen balance
- ZGC: Aplicaciones con requisitos de baja latencia
- Parallel: Procesamiento batch donde throughput importa más que latencia
Cómo responder en entrevista:
“El Garbage Collector es un proceso automático de la JVM que libera memoria de objetos no accesibles. Un objeto es elegible para GC cuando no tiene referencias apuntándole. Java usa un modelo generacional: objetos nuevos van al Young Generation y si sobreviven varios ciclos pasan al Old Generation. El GC por defecto desde Java 9 es G1, que ofrece buen balance entre throughput y latencia. Para aplicaciones con requisitos de ultra-baja latencia, ZGC es la mejor opción.”