Introducción a Java Avanzado
En este módulo explorarás características avanzadas de Java que te permitirán escribir código más eficiente, seguro y moderno. Estas herramientas son esenciales para el desarrollo de aplicaciones profesionales.
Genéricos
Tipos parametrizados para código reutilizable y type-safe
Lambda
Funciones anónimas para programación funcional
Streams API
Procesamiento declarativo de colecciones
Fechas/Horas
API moderna para manejo de fechas (Java 8+)
Archivos
Lectura y escritura eficiente de archivos
Genéricos (Generics)
Tipos Parametrizados: Write Once, Use Any Type
// CLASE GENÉRICA BÁSICA
public class Caja {
private T contenido;
public void guardar(T contenido) {
this.contenido = contenido;
}
public T obtener() {
return contenido;
}
public void mostrarTipo() {
System.out.println("Tipo de T: " + contenido.getClass().getSimpleName());
}
}
// CLASE GENÉRICA CON MÚLTIPLES PARÁMETROS
public class Par {
private K clave;
private V valor;
public Par(K clave, V valor) {
this.clave = clave;
this.valor = valor;
}
public K getClave() { return clave; }
public V getValor() { return valor; }
}
// MÉTODO GENÉRICO
public class Utilidades {
public static boolean esIgual(T obj1, T obj2) {
return obj1.equals(obj2);
}
public static > T maximo(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
}
Comodines (Wildcards)
Upper Bounded
Acepta el tipo especificado o sus subclases
List extends Number> lista;
Lower Bounded
Acepta el tipo especificado o sus superclases
List super Integer> lista;
Unbounded
Acepta cualquier tipo (solo lectura)
List> lista;
Expresiones Lambda
Programación Funcional en Java
Las expresiones lambda permiten implementar interfaces funcionales de forma concisa:
// INTERFAZ FUNCIONAL (un solo método abstracto)
@FunctionalInterface
interface Operacion {
int ejecutar(int a, int b);
}
// FORMA TRADICIONAL (clase anónima)
Operacion suma = new Operacion() {
@Override
public int ejecutar(int a, int b) {
return a + b;
}
};
// FORMA LAMBDA (equivalente)
Operacion suma = (a, b) -> a + b;
Laboratorio de Lambda
Interfaces Funcionales Comunes
Representa una función que recibe un argumento y devuelve boolean
Predicate esLargo = s -> s.length() > 10;
Recibe un argumento y produce un resultado
Function longitud = s -> s.length();
Realiza una operación con el argumento (sin retornar)
Consumer imprimir = s -> System.out.println(s);
Provee/suministra un resultado (sin recibir argumentos)
Supplier random = () -> Math.random();
Streams API
Procesamiento Declarativo de Colecciones
Origen (Source)
Crear un Stream desde una colección
List lista = Arrays.asList("a", "b", "c");
Stream stream = lista.stream();
Operaciones Intermedias
Transformar o filtrar elementos
stream.filter(s -> s.startsWith("a"))
.map(String::toUpperCase);
Operación Terminal
Obtener resultado final
List resultado = stream.collect(Collectors.toList());
Simulador de Streams
Datos de Entrada (separados por comas):
Operaciones Intermedias:
Operación Terminal:
Fuente Original
Operaciones Intermedias
Resultado Final
Ejemplos Prácticos de Streams
Filtrar y Transformar
List nombres = Arrays.asList("Ana", "Juan", "María");
List resultado = nombres.stream()
.filter(n -> n.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.toList());
// Resultado: ["MARÍA"]
Operaciones Numéricas
List numeros = Arrays.asList(1, 2, 3, 4, 5);
int suma = numeros.stream()
.mapToInt(Integer::intValue)
.sum();
// Resultado: 15
Agrupación
List palabras = Arrays.asList("java", "python", "java", "c++");
Map conteo = palabras.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
// Resultado: {java=2, python=1, c++=1}
Fechas y Horas (Java 8+)
API de Fechas Moderna (java.time)
Antiguo (java.util.Date) - ❌ NO RECOMENDADO
Date fecha = new Date(); // Mutabilidad
Calendar cal = Calendar.getInstance();
cal.set(2024, 0, 1); // Enero es 0
Date añoNuevo = cal.getTime();
Mutabilidad (no thread-safe)
API confusa (meses de 0 a 11)
No zona horaria por defecto
Moderno (java.time) - ✅ RECOMENDADO
LocalDate hoy = LocalDate.now(); // Inmutabilidad
LocalDate añoNuevo = LocalDate.of(2024, 1, 1);
LocalTime ahora = LocalTime.now();
LocalDateTime fechaHora = LocalDateTime.now();
Inmutabilidad (thread-safe)
API clara y consistente
Zonas horarias integradas
Laboratorio de Fechas
LocalDate
LocalTime
LocalDateTime
ZonedDateTime
Formato y Parseo de Fechas
De String a Date
// Parseo de fecha
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate fecha = LocalDate.parse("15/01/2024", formatter);
De Date a String
// Formateo de fecha
LocalDate hoy = LocalDate.now();
String formato = hoy.format(
DateTimeFormatter.ofPattern("EEEE dd 'de' MMMM 'de' yyyy")
);
// Resultado: "lunes 15 de enero de 2024"
Duración y Período
// Diferencia entre fechas
LocalDate inicio = LocalDate.of(2024, 1, 1);
LocalDate fin = LocalDate.of(2024, 12, 31);
Period periodo = Period.between(inicio, fin);
// Resultado: P11M30D (11 meses, 30 días)
Manejo de Archivos
Lectura y Escritura Eficiente
Lectura Básica (FileReader)
// Lectura básica (poco eficiente)
FileReader fr = new FileReader("archivo.txt");
int caracter;
while ((caracter = fr.read()) != -1) {
System.out.print((char) caracter);
}
fr.close();
Lectura carácter por carácter
No bufferizado
Lectura Eficiente (BufferedReader)
// Lectura eficiente con buffer
BufferedReader br = new BufferedReader(
new FileReader("archivo.txt")
);
String linea;
while ((linea = br.readLine()) != null) {
System.out.println(linea);
}
br.close();
Bufferizado (alto rendimiento)
Lectura línea por línea
Lectura Moderna (Java 7+)
// try-with-resources (auto-close)
try (BufferedReader br = Files.newBufferedReader(
Paths.get("archivo.txt")
)) {
br.lines().forEach(System.out::println);
} // No necesita close() explícito
Auto-cerrable
Uso de Streams
Editor de Archivos Virtual
Contenido del Archivo Virtual
Escribir Nuevo Contenido
Añadir al Final del Archivo
Operaciones de Archivo:
Ejemplos Prácticos
Leer Archivo CSV
// Leer y procesar archivo CSV
try (BufferedReader br = Files.newBufferedReader(
Paths.get("datos.csv")
)) {
br.lines()
.skip(1) // Saltar cabecera
.map(linea -> linea.split(","))
.filter(campos -> campos.length == 3)
.forEach(campos -> {
String nombre = campos[0];
int edad = Integer.parseInt(campos[1]);
String ciudad = campos[2];
// Procesar datos...
});
}
Escribir Archivo Log
// Escribir archivo de log
try (BufferedWriter bw = Files.newBufferedWriter(
Paths.get("app.log"),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND
)) {
LocalDateTime ahora = LocalDateTime.now();
String mensaje = String.format(
"[%s] %s: %s%n",
ahora.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
"INFO",
"Aplicación iniciada correctamente"
);
bw.write(mensaje);
}
Copiar Archivo
// Copiar archivo eficientemente
Path origen = Paths.get("original.txt");
Path destino = Paths.get("copia.txt");
try (InputStream in = Files.newInputStream(origen);
OutputStream out = Files.newOutputStream(destino)) {
byte[] buffer = new byte[1024];
int bytesLeidos;
while ((bytesLeidos = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesLeidos);
}
}
Ejercicios Integrados
Sistema de Inventario con Genéricos
Crea un sistema de inventario que use genéricos para manejar diferentes tipos de productos:
- Clase genérica
Almacen<T>que almacene productos de tipo T - Interface
Productocon métodosgetPrecio()ygetNombre() - Clases
ProductoFisicoyProductoDigitalque implementen Producto - Método genérico para calcular valor total del inventario
- Uso de wildcards para métodos que leen productos
Procesador de Texto con Lambdas y Streams
Implementa un procesador de texto que use lambdas y streams para análisis de texto:
- Leer archivo de texto usando
Files.lines() - Usar lambdas para filtrar, transformar y reducir
- Calcular estadísticas: palabras más comunes, longitud promedio
- Generar reporte con formato usando fechas
- Escribir resultados en archivo usando
BufferedWriter
Gestor de Eventos con API de Fechas
Crea un sistema para gestionar eventos usando la API de fechas moderna:
- Clase
Eventocon nombre, fecha/hora y duración - Usar
LocalDateTimeyDuration - Filtrar eventos por fecha usando lambdas
- Calcular tiempos restantes para eventos futuros
- Ordenar eventos cronológicamente usando streams
- Exportar agenda a archivo CSV
Sistema de Logs Avanzado
Implementa un sistema de logging que combine todas las características avanzadas:
- Clase genérica
Logger<T>para diferentes tipos de mensajes - Usar lambdas para formateo personalizado de mensajes
- Streams para análisis de logs (frecuencia de errores, etc.)
- API de fechas para timestamp de cada entrada
- Rotación de archivos de log por fecha
- Búsqueda eficiente en logs usando streams paralelos
Quiz de Conceptos Avanzados
Pregunta 1: ¿Qué ventaja principal ofrecen los genéricos en Java?
Resumen y Recursos
Lo Aprendido en Este Módulo
Genéricos
- Clases y métodos parametrizados
- Type safety en tiempo de compilación
- Wildcards: extends, super, unbounded
- Eliminan necesidad de casts explícitos
Expresiones Lambda
- Sintaxis concisa para interfaces funcionales
- Programación funcional en Java
- Interfaces comunes: Predicate, Function, Consumer, Supplier
- Method references (::)
Streams API
- Procesamiento declarativo de colecciones
- Operaciones intermedias y terminales
- Pipelines de procesamiento
- Streams paralelos para procesamiento concurrente
Fechas y Horas
- API java.time (inmutable y thread-safe)
- LocalDate, LocalTime, LocalDateTime
- ZonedDateTime para zonas horarias
- Period y Duration para cálculos
Manejo de Archivos
- Lectura/escritura eficiente con buffers
- Try-with-resources para auto-close
- NIO.2 para operaciones de archivos modernas
- Procesamiento de archivos con streams