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 lista;
Lower Bounded

Acepta el tipo especificado o sus superclases

List 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
Predicate<T>

Representa una función que recibe un argumento y devuelve boolean

Predicate esLargo = s -> s.length() > 10;
Function<T,R>

Recibe un argumento y produce un resultado

Function longitud = s -> s.length();
Consumer<T>

Realiza una operación con el argumento (sin retornar)

Consumer imprimir = s -> System.out.println(s);
Supplier<T>

Provee/suministra un resultado (sin recibir argumentos)

Supplier random = () -> Math.random();

Streams API

Procesamiento Declarativo de Colecciones

1
Origen (Source)

Crear un Stream desde una colección

List lista = Arrays.asList("a", "b", "c");
Stream stream = lista.stream();
2
Operaciones Intermedias

Transformar o filtrar elementos

stream.filter(s -> s.startsWith("a"))
      .map(String::toUpperCase);
3
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:
Tamaño: 0 bytes
Líneas: 0
Palabras: 0
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:

  1. Clase genérica Almacen<T> que almacene productos de tipo T
  2. Interface Producto con métodos getPrecio() y getNombre()
  3. Clases ProductoFisico y ProductoDigital que implementen Producto
  4. Método genérico para calcular valor total del inventario
  5. 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:

  1. Leer archivo de texto usando Files.lines()
  2. Usar lambdas para filtrar, transformar y reducir
  3. Calcular estadísticas: palabras más comunes, longitud promedio
  4. Generar reporte con formato usando fechas
  5. 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:

  1. Clase Evento con nombre, fecha/hora y duración
  2. Usar LocalDateTime y Duration
  3. Filtrar eventos por fecha usando lambdas
  4. Calcular tiempos restantes para eventos futuros
  5. Ordenar eventos cronológicamente usando streams
  6. Exportar agenda a archivo CSV

Sistema de Logs Avanzado

Implementa un sistema de logging que combine todas las características avanzadas:

  1. Clase genérica Logger<T> para diferentes tipos de mensajes
  2. Usar lambdas para formateo personalizado de mensajes
  3. Streams para análisis de logs (frecuencia de errores, etc.)
  4. API de fechas para timestamp de cada entrada
  5. Rotación de archivos de log por fecha
  6. Búsqueda eficiente en logs usando streams paralelos

Quiz de Conceptos Avanzados

Pregunta 1: ¿Qué ventaja principal ofrecen los genéricos en Java?

Permiten escribir código más rápido
Proporcionan type safety y reutilización de código
Hacen que el código sea más legible para principiantes
Eliminan la necesidad de usar excepciones
1 de 5

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

Descargar Recursos