RESUMEN JAVA ENTREVISTAS v2
RESUMEN JAVA ENTREVISTAS v2
• Tipos de datos:
Concepto: Los tipos de datos en Java definen el tipo de valores que
pueden ser almacenados y manipulados en una variable
Lo más importante: Java tiene tipos de datos primitivos (8) (byte,
short, int, long, float, double, char y boolean) y tipos de datos de
objeto (como String, Integer, etc.).
Los tipos de datos primitivos almacenan valores directamente,
mientras que los tipos de datos de objeto almacenan referencias a
objetos en la memoria.
Es importante entender los límites y el almacenamiento de los tipos
de datos primitivos para evitar desbordamientos y errores de
precisión.
Ejemplo:
int entero = 10;
double decimal = 3.14;
String texto = "Hola mundo";
Ejemplo:
• Estructuras repetitivas: Object objeto = "Hola";
Concepto: Las estructuras repetitivas en Java permiten ejecutar un String cadena = (String) objeto;
bloque de código varias veces.
Lo más importante: Las estructuras repetitivas más comunes son for, • Operador instanceof:
while y do-while. Concepto: El operador instanceof en Java se utiliza para verificar si
▪ for: Se utiliza cuando se conoce el número de iteraciones. Es útil un objeto es una instancia de una clase, subclase o interfaz.
para iterar sobre una secuencia o colección de elementos. Lo más importante: Devuelve true si el objeto es una instancia de la
▪ while: Se utiliza cuando la condición de bucle se evalúa antes clase especificada, de lo contrario, devuelve false.
de cada iteración. Puede no ejecutarse si la condición Ejemplo:
inicialmente es falsa. Object objeto = "Hola";
▪ do-while: Similar a while, pero garantiza que el bloque de if (objeto instanceof String) {
código se ejecute al menos una vez antes de evaluar la System.out.println("El objeto es una instancia de String");
condición. }
• Error vs. Excepción: System.out.println("El perro ladra");
Concepto: En Java, un error es una situación grave que no debería }
ser manejada en tiempo de ejecución, mientras que una excepción }
es una situación excepcional que puede ser manejada en tiempo de
ejecución. Uso de Herencia:
Lo más importante: Los errores son irreparables y generalmente se Teoría: La herencia es un concepto en el que una clase (subclase)
deben a problemas fuera del control del programador, mientras que puede heredar atributos y métodos de otra clase (superclase).
las excepciones son eventos inesperados que pueden y deben ser Lo más importante: Permite la reutilización de código y la creación
manejados. de jerarquías de clases.
Ejemplo: Un error de OutOfMemoryError ocurre cuando no hay Ejemplo:
suficiente memoria disponible para ejecutar una aplicación, class Vehiculo {
mientras que una excepción NullPointerException ocurre cuando se void conducir() {
intenta acceder a un objeto nulo. Las excepciones en Java se pueden System.out.println("Conduciendo el vehículo");
manejar utilizando bloques try-catch }
}
• Propagación de excepciones vs. Lanzamiento de
excepción: class Coche extends Vehiculo {
Concepto: La propagación de excepciones en Java es el proceso de // Hereda el método conducir de la clase Vehiculo
pasar una excepción de un método a su llamador, mientras que el }
lanzamiento de excepción es el proceso de generar manualmente
una excepción. Uso de Encapsulación:
Lo más importante: Las excepciones pueden propagarse Teoría: La encapsulación es el mecanismo de ocultar los detalles de
automáticamente a través de las llamadas de método en Java, o implementación de una clase y restringir el acceso directo a los
pueden ser lanzadas explícitamente con la palabra clave throw. campos de datos.
Ejemplo: Lo más importante: Se utiliza para garantizar la coherencia de los
public void metodo1() throws Exception { datos y para proteger los datos sensibles de modificaciones no
// deseadas.
Overloading (Sobrecarga): Ejemplo:
Teoría: Overloading se refiere a la capacidad de definir varios class Persona {
métodos en una clase con el mismo nombre pero diferentes tipos de private String nombre;
parámetros o número de parámetros. private int edad;
Lo más importante: Los métodos sobrecargados deben diferir en su // Métodos getter/ setter para acceder a los campos encapsulados
firma, es decir, en el número o tipo de parámetros. public String getNombre() {
Ejemplo: return nombre;
class Calculadora { }
int sumar(int a, int b) { public void setNombre(String nombre) {
return a + b; this.nombre = nombre;
} }
// Otros métodos de la clase...
double sumar(double a, double b) { }
return a + b;
} Uso de Polimorfismo:
} Teoría: El polimorfismo se refiere a la capacidad de un objeto de
tomar muchas formas. En Java, se logra mediante la sobrecarga y la
Overriding (Sobreescritura): sobreescritura.
Teoría: Overriding se refiere a la capacidad de una subclase de Lo más importante: Permite tratar a objetos de diferentes clases de
proporcionar una implementación específica de un método que ya manera uniforme a través de una interfaz común.
está definido en su superclase. Ejemplo:
Lo más importante: El método en la subclase debe tener la misma class Figura {
firma que el método en la superclase. void dibujar() {
Ejemplo: System.out.println("Dibujando una figura");
class Animal { }
void sonido() { }
System.out.println("Animal hace un sonido"); class Circulo extends Figura {
} @Override
} void dibujar() {
System.out.println("Dibujando un círculo");
class Perro extends Animal { }
@Override }
void sonido() {
class Cuadrado extends Figura { abiertas para la extensión pero cerradas para la
@Override modificación. Ejemplo en Spring Boot:
void dibujar() { En una aplicación de gestión de productos, se pueden agregar
System.out.println("Dibujando un cuadrado"); nuevos tipos de productos sin modificar el código existente. Por
} ejemplo, podemos tener una interfaz ProductService y
} diferentes implementaciones para manejar diferentes tipos de
productos.
Uso de Abstracción: @Service
Teoría: La abstracción es un principio de diseño que se centra en los public interface ProductService {
aspectos esenciales de un objeto y oculta los detalles innecesarios. void addProduct(Product product);
Lo más importante: Permite modelar entidades del mundo real de }
manera más precisa y simplifica la complejidad del sistema.
Ejemplo: @Service
abstract class Animal { public class ElectronicProductService implements
abstract void sonido(); // Método abstracto que define el sonido ProductService {
del animal @Override
} public void addProduct(Product product) {
class Perro extends Animal { // Lógica para agregar un producto electrónico
@Override }
void sonido() { }
System.out.println("El perro ladra");
} @Service
} public class ClothingProductService implements ProductService
{
Uso de Generics <T>: @Override
Teoría: Los generics en Java permiten crear clases, interfaces y public void addProduct(Product product) {
métodos que funcionan con tipos específicos (parametrizados) que // Lógica para agregar un producto de ropa
se determinan en el momento de la compilación. }
Lo más importante: Proporcionan seguridad de tipo en tiempo de }
compilación y evitan la necesidad de realizar casting. Importancia: Facilita la extensión y mantenimiento del sistema
Ejemplo: sin modificar el código existente.
class Contenedor<T> { Beneficios:
Mejora la modularidad y reutilización del código.
Principios SOLID: Reduce el riesgo de introducir errores en el código existente.
SRP (Principio de responsabilidad única): Una clase debe tener
una única razón para cambiar. Es decir, cada clase debe tener una LSP (Principio de sustitución de Liskov): Los objetos de un
sola responsabilidad. Ejemplo en Spring Boot: programa deben ser reemplazables por instancias de sus
Supongamos que tenemos una clase UserService que se encarga subtipos sin afectar la corrección del programa.
de gestionar la lógica de negocio relacionada con los usuarios,
como la autenticación, el registro y la actualización de datos. ISP (Principio de segregación de la interfaz): Los clientes no deben
@Service verse obligados a depender de interfaces que no utilizan.
public class UserService {
@Autowired DIP (Principio de inversión de dependencias): Los módulos de alto
private UserRepository userRepository; nivel no deben depender de módulos de bajo nivel. Ambos deben
public void registerUser(User user) { depender de abstracciones. Se basa en el uso de interfaces o
// Lógica para registrar un usuario clases abstractas para definir contratos entre componentes del
} sistema.
public void updateUser(User user) {
// Lógica para actualizar un usuario Patrón de Diseño Fábrica/Factory:
} Teoría: El patrón de diseño Fábrica proporciona una interfaz para
// Otros métodos relacionados con la gestión de usuarios crear objetos de un tipo, pero permite a las subclases alterar el tipo
} de objetos que se crearán.
Importancia: Al tener una sola responsabilidad, la clase Lo más importante: Separación de la creación de objetos de su uso.
UserService es más fácil de entender, mantener y probar. Ejemplo: Una interfaz Shape con implementaciones Circle y Square.
Beneficios: La fábrica ShapeFactory devuelve instancias de estas clases según el
Facilita la evolución y mantenimiento del código. tipo solicitado.
Mejora la cohesión y modularidad del sistema. ¿Qué es el Patron Factory? - Arquitectura Java
METHOD REFERENCES:
Teoría:
List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5);
Las referencias a métodos son una forma de referirse a un método
existente por su nombre. Proporcionan una forma concisa de
// Secuencial
expresar expresiones lambda que invocan un único método
numeros.stream().forEach(System.out::println);
existente.
Lo más importante:
// Paralelo
Se pueden utilizar para referenciar métodos estáticos, métodos de
numeros.parallelStream().forEach(System.out::println);
instancia y constructores.
Proporcionan un código más limpio y legible al reutilizar métodos
En Java, el método findAny() es parte de la interfaz Stream. Este
existentes.
método se utiliza para encontrar cualquier elemento dentro de un
Ejemplo:
flujo (stream) de elementos. La especificación de Java no garantiza
import java.util.function.Function;
qué elemento será devuelto; puede ser cualquier elemento del flujo,
dependiendo de la implementación y del estado interno del flujo.
public class Main {
public static String convertToUpper(String input) {
return input.toUpperCase();
CLASE OPTIONAL:
}
Teoría:
Optional es una clase introducida en Java 8 para representar un
public static void main(String[] args) {
contenedor que puede o no contener un valor. Se utiliza para evitar
Function<String, String> toUpperCase = Main::convertToUpper;
las NullPointerException y proporcionar una forma más clara de
System.out.println(toUpperCase.apply("hello")); // Output:
trabajar con valores potencialmente nulos.
HELLO
}
Optional nos proporciona otra manera de lidiar con este error
evitándonos cachar las excepciones (lo cual ahorra costo en
rendimiento) y en lugar de eso controlar la ausencia de valor
permitiéndonos si así fuera el caso elegir un valor alternativo o
simplemente realizar otra acción.
Lo más importante:
Se puede utilizar para evitar las comprobaciones de null.
Proporciona métodos como isPresent, orElse, orElseThrow para
trabajar de manera segura con valores opcionales.
Ejemplo:
import java.util.Optional;
HTTPS
Son cancelables
Pueden ser finito o infinito.
Patrón SAGA
El Patrón SAGA aplicado a microservicios nos ayuda a garantizar la integridad
en transaccione distribuidas repartidas entre microservicios.
Tipos de CQRS
CQRS Básico: En su forma más simple, CQRS implica simplemente
separar los modelos de lectura y escritura en tu aplicación. Las
operaciones de lectura consultan un modelo que está optimizado
para consultas rápidas y eficientes, mientras que las operaciones de
escritura actualizan un modelo que está optimizado para la
O Motores BPM como Camunda que por su ligereza son validación y la consistencia de las transacciones.
perfectamente válidos para hacer esta orquestación, podéis verlo en CQRS con Event Sourcing: En este enfoque, además de separar los
este ejemplo: modelos de lectura y escritura, también se utiliza el patrón de Event
Sourcing. Con Event Sourcing, en lugar de almacenar el estado actual
de tus datos, almacenas una secuencia de eventos que representan
cambios en tus datos a lo largo del tiempo. Esto puede hacer que sea
más fácil mantener la consistencia entre tus modelos de lectura y
escritura, y también puede proporcionar beneficios adicionales,
como la capacidad de "rebobinar" tus datos a un estado anterior.
Ventajas
Optimización independiente: Al separar las operaciones de lectura y
escritura, puedes optimizar cada una de ellas de manera
independiente. Por ejemplo, puedes optimizar el modelo de lectura
para consultas rápidas y eficientes, mientras que el modelo de
escritura puede estar optimizado para la validación y la consistencia
de las transacciones.
Escalabilidad: CQRS puede ayudar a mejorar la escalabilidad de tu
PATRON CQRS aplicación. Si tienes una carga de trabajo con muchas más lecturas
que escrituras (o viceversa), puedes escalar los modelos de lectura y
CQRS significa Command Query Responsibility Segregation escritura de forma independiente para satisfacer tus necesidades.
(Segregación de Responsabilidades de Comandos y Consultas). Es un Flexibilidad: CQRS te permite cambiar el modelo de lectura y
patrón de diseño de software que separa las operaciones de lectura escritura de forma independiente. Esto puede ser útil si necesitas
(consultas) y escritura (comandos) en diferentes modelos. cambiar la forma en que manejas las consultas o las actualizaciones,
En un sistema tradicional, utilizamos el mismo modelo de dominio pero no quieres afectar la otra parte de tu aplicación.
para leer y actualizar la base de datos. Pero en el patrón CQRS, Seguridad: Puedes aplicar diferentes políticas de seguridad a las
separamos estos dos aspectos en diferentes modelos. Esto permite operaciones de lectura y escritura. Por ejemplo, puedes permitir a
optimizar cada modelo para la tarea que realiza, lo que puede ciertos usuarios realizar consultas pero no actualizaciones.
resultar en un rendimiento mejorado, una mayor escalabilidad y una Complejidad reducida: Al dividir el sistema en dos partes más
mayor flexibilidad. pequeñas y más manejables, puedes reducir la complejidad de cada
Por ejemplo, el modelo de escritura puede estar optimizado para la parte.
validación de reglas de negocio y la consistencia de transacciones, Desventajas
mientras que el modelo de lectura puede estar optimizado para la Complejidad adicional: CQRS puede añadir una capa de complejidad
eficiencia de las consultas y la presentación de datos. a tu aplicación. Tienes que mantener dos modelos diferentes y
asegurarte de que estén sincronizados. Esto puede hacer que el
Es importante tener en cuenta que CQRS es un patrón avanzado y código sea más difícil de entender y mantener.
puede añadir complejidad a la aplicación. Por lo tanto, debe Consistencia eventual: Como las operaciones de lectura y escritura
utilizarse cuando el beneficio que aporta supera el costo de esa están separadas, puedes terminar con una consistencia eventual,
complejidad adicional. donde los datos leídos pueden no reflejar las últimas escrituras
inmediatamente. Esto puede ser confuso para los usuarios si no se Contenedorización: Docker utiliza contenedores para empaquetar y
maneja correctamente. distribuir aplicaciones junto con sus dependencias y bibliotecas, pero
Dificultad de prueba: Probar un sistema CQRS puede ser más difícil comparten el mismo kernel del sistema operativo anfitrión.
debido a la necesidad de coordinar y verificar el comportamiento
entre los modelos de lectura y escritura. Sistema Operativo Base Compartido: Todos los contenedores Docker
Costo de desarrollo y mantenimiento: Implementar CQRS puede comparten el mismo kernel del sistema operativo anfitrión, lo que
requerir más tiempo y esfuerzo que un enfoque tradicional. También reduce la sobrecarga en comparación con las máquinas virtuales.
puede aumentar los costos de mantenimiento a largo plazo.
No es adecuado para todas las aplicaciones: CQRS puede ser útil en Uso de Recursos: Los contenedores son más ligeros en términos de
aplicaciones con requisitos complejos de dominio o donde las recursos en comparación con las máquinas virtuales, ya que no
operaciones de lectura y escritura tienen diferentes necesidades de necesitan cargar un sistema operativo completo para cada
escalabilidad. Sin embargo, para aplicaciones más simples, puede ser contenedor.
excesivo y no proporcionar beneficios significativos.
Caso Práctico - API Customers Tiempo de Arranque: Los contenedores Docker son más rápidos de
Problema: arrancar en comparación con las máquinas virtuales, ya que no
Dependencia de consumidores (30) para hacer las pruebas de necesitan cargar un sistema operativo completo.
cambio y regresión, son bastantes y el tiempo que se toman en
probar es de 1 mes aproximadamente. Aislamiento: Los contenedores Docker proporcionan un nivel de
Solución: aislamiento más bajo en comparación con las máquinas virtuales, ya
Implementar el CQRS Básico. que comparten el mismo kernel del sistema operativo anfitrión,
Separar el microservicio customers en 2: aunque siguen ofreciendo un alto grado de aislamiento de recursos.
customers-quey: Agrupa los endpoints de consulta (29
consumidores). En resumen, Docker se centra en la contenedorización de
customers-command: Agrupa los endpoints que persisten en la aplicaciones, lo que permite un despliegue rápido y portátil de
BBDD, actualización y creación (10 consumidores). aplicaciones junto con sus dependencias, mientras que las máquinas
virtuales proporcionan un aislamiento más completo y la capacidad
de ejecutar múltiples sistemas operativos completos en un mismo
hardware físico.
JUNIT
Gateway: el cual sirve como api Gateway como una única puerta de
enlace para acceder
Ribbon: Permite manejar el balanceo de carga de los microservicios
Eureka Server: Permite registrar los microservicios y el
descubrimiento
Hystrix: permite implementar el patrón circuit breaker para la
tolerancia a fallos
Spring Config: Sirve para implementar una configuración
centralizada de los microservicios.
Logging: Log centralizado