Java 8, lanzado en marzo de 2014, introdujo una serie de características nuevas y poderosas que han transformado la forma en que los desarrolladores escriben código en Java. Este módulo cubrirá las características más importantes de Java 8, proporcionando explicaciones detalladas, ejemplos prácticos y ejercicios para reforzar el aprendizaje.
Contenido
Expresiones Lambda
Introducción
Las expresiones lambda son una característica clave de Java 8 que permiten tratar la funcionalidad como un argumento del método o almacenar la funcionalidad en una variable. Las lambdas proporcionan una forma clara y concisa de representar un método anónimo.
Sintaxis
o
Ejemplo
// Ejemplo de una expresión lambda simple
Runnable runnable = () -> System.out.println("Hola, Mundo!");
runnable.run();Explicación
En este ejemplo, () -> System.out.println("Hola, Mundo!") es una expresión lambda que implementa la interfaz Runnable.
Ejercicio
Escribe una expresión lambda que tome dos enteros y devuelva su suma.
@FunctionalInterface
interface Suma {
int sumar(int a, int b);
}
public class LambdaExample {
public static void main(String[] args) {
Suma suma = (a, b) -> a + b;
System.out.println("Suma: " + suma.sumar(5, 3)); // Salida: Suma: 8
}
}Streams
Introducción
Los Streams en Java 8 proporcionan una forma moderna y funcional de procesar colecciones de datos. Permiten realizar operaciones como filtrado, mapeo y reducción de manera declarativa.
Ejemplo
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria");
nombres.stream()
.filter(nombre -> nombre.startsWith("P"))
.forEach(System.out::println); // Salida: Pedro
}
}Explicación
En este ejemplo, nombres.stream() crea un stream a partir de la lista nombres. Luego, filter(nombre -> nombre.startsWith("P")) filtra los nombres que comienzan con "P", y forEach(System.out::println) imprime cada nombre filtrado.
Ejercicio
Usa Streams para encontrar el número máximo en una lista de enteros.
import java.util.Arrays;
import java.util.List;
public class MaxExample {
public static void main(String[] args) {
List<Integer> numeros = Arrays.asList(3, 5, 7, 2, 8);
int max = numeros.stream()
.max(Integer::compare)
.get();
System.out.println("Máximo: " + max); // Salida: Máximo: 8
}
}Interfaz Funcional
Introducción
Una interfaz funcional es una interfaz que contiene exactamente un método abstracto. Estas interfaces pueden ser implementadas por expresiones lambda.
Ejemplo
@FunctionalInterface
interface Operacion {
int operar(int a, int b);
}
public class FunctionalInterfaceExample {
public static void main(String[] args) {
Operacion suma = (a, b) -> a + b;
System.out.println("Suma: " + suma.operar(10, 5)); // Salida: Suma: 15
}
}Explicación
En este ejemplo, Operacion es una interfaz funcional con un método abstracto operar. La expresión lambda (a, b) -> a + b implementa esta interfaz.
Ejercicio
Crea una interfaz funcional que tenga un método que reciba un string y devuelva su longitud.
@FunctionalInterface
interface Longitud {
int obtenerLongitud(String s);
}
public class LongitudExample {
public static void main(String[] args) {
Longitud longitud = s -> s.length();
System.out.println("Longitud: " + longitud.obtenerLongitud("Hola")); // Salida: Longitud: 4
}
}Métodos Predeterminados y Estáticos en Interfaces
Introducción
Java 8 permite definir métodos predeterminados y estáticos en interfaces. Los métodos predeterminados proporcionan una implementación predeterminada que las clases pueden usar o sobrescribir.
Ejemplo
interface Vehiculo {
default void encender() {
System.out.println("El vehículo está encendido.");
}
static void revisar() {
System.out.println("Revisando el vehículo.");
}
}
public class Carro implements Vehiculo {
public static void main(String[] args) {
Carro carro = new Carro();
carro.encender(); // Salida: El vehículo está encendido.
Vehiculo.revisar(); // Salida: Revisando el vehículo.
}
}Explicación
En este ejemplo, Vehiculo tiene un método predeterminado encender y un método estático revisar. La clase Carro puede usar estos métodos directamente.
Ejercicio
Crea una interfaz con un método predeterminado que imprima un mensaje de saludo y un método estático que imprima un mensaje de despedida.
interface Saludo {
default void saludar() {
System.out.println("Hola, bienvenido!");
}
static void despedir() {
System.out.println("Adiós, hasta luego!");
}
}
public class SaludoExample implements Saludo {
public static void main(String[] args) {
SaludoExample ejemplo = new SaludoExample();
ejemplo.saludar(); // Salida: Hola, bienvenido!
Saludo.despedir(); // Salida: Adiós, hasta luego!
}
}API de Fecha y Hora
Introducción
Java 8 introdujo una nueva API de fecha y hora en el paquete java.time, que es más intuitiva y menos propensa a errores que las clases Date y Calendar anteriores.
Ejemplo
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
public class DateTimeExample {
public static void main(String[] args) {
LocalDate fecha = LocalDate.now();
LocalTime hora = LocalTime.now();
LocalDateTime fechaHora = LocalDateTime.now();
System.out.println("Fecha: " + fecha); // Salida: Fecha: 2023-10-01 (ejemplo)
System.out.println("Hora: " + hora); // Salida: Hora: 10:15:30.123 (ejemplo)
System.out.println("Fecha y Hora: " + fechaHora); // Salida: Fecha y Hora: 2023-10-01T10:15:30.123 (ejemplo)
}
}Explicación
En este ejemplo, LocalDate.now(), LocalTime.now(), y LocalDateTime.now() obtienen la fecha, hora y fecha y hora actuales, respectivamente.
Ejercicio
Crea un programa que imprima la fecha y hora actuales en un formato específico.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeFormatExample {
public static void main(String[] args) {
LocalDateTime fechaHora = LocalDateTime.now();
DateTimeFormatter formato = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
String fechaHoraFormateada = fechaHora.format(formato);
System.out.println("Fecha y Hora Formateada: " + fechaHoraFormateada); // Salida: Fecha y Hora Formateada: 01-10-2023 10:15:30 (ejemplo)
}
}Referencias a Métodos
Introducción
Las referencias a métodos proporcionan una forma de referenciar métodos existentes sin tener que invocarlos explícitamente. Son una forma abreviada de expresiones lambda.
Ejemplo
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria");
nombres.forEach(System.out::println); // Salida: Ana, Juan, Pedro, Maria
}
}Explicación
En este ejemplo, System.out::println es una referencia a método que reemplaza la expresión lambda nombre -> System.out.println(nombre).
Ejercicio
Usa una referencia a método para ordenar una lista de cadenas en orden alfabético.
import java.util.Arrays;
import java.util.List;
public class MethodReferenceSortExample {
public static void main(String[] args) {
List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria");
nombres.sort(String::compareToIgnoreCase);
nombres.forEach(System.out::println); // Salida: Ana, Juan, Maria, Pedro
}
}Opcional
Introducción
La clase Optional se introdujo en Java 8 para manejar valores que pueden estar presentes o no, evitando así el uso excesivo de null y reduciendo la posibilidad de errores NullPointerException.
Ejemplo
import java.util.Optional;
public class OptionalExample {
public static void main(String[] args) {
Optional<String> nombre = Optional.of("Juan");
nombre.ifPresent(System.out::println); // Salida: Juan
Optional<String> nombreVacio = Optional.empty();
System.out.println(nombreVacio.orElse("Nombre no disponible")); // Salida: Nombre no disponible
}
}Explicación
En este ejemplo, Optional.of("Juan") crea un Optional que contiene el valor "Juan". Optional.empty() crea un Optional vacío. ifPresent y orElse son métodos útiles para manejar valores opcionales.
Ejercicio
Crea un programa que use Optional para manejar un valor que puede estar presente o no, y proporciona un valor predeterminado si no está presente.
import java.util.Optional;
public class OptionalDefaultExample {
public static void main(String[] args) {
Optional<String> mensaje = Optional.ofNullable(null);
String mensajeFinal = mensaje.orElse("Mensaje predeterminado");
System.out.println(mensajeFinal); // Salida: Mensaje predeterminado
}
}Conclusión
En este módulo, hemos explorado las características más importantes de Java 8, incluyendo expresiones lambda, streams, interfaces funcionales, métodos predeterminados y estáticos en interfaces, la nueva API de fecha y hora, referencias a métodos y la clase Optional. Estas características no solo hacen que el código sea más conciso y legible, sino que también introducen un enfoque más funcional en la programación en Java. Asegúrate de practicar con los ejemplos y ejercicios proporcionados para consolidar tu comprensión de estos conceptos. ¡Prepárate para el siguiente módulo donde exploraremos más características avanzadas de Java!
Curso de Programación en Java
Módulo 1: Introducción a Java
- Introducción a Java
- Configuración del Entorno de Desarrollo
- Sintaxis y Estructura Básica
- Variables y Tipos de Datos
- Operadores
Módulo 2: Flujo de Control
Módulo 3: Programación Orientada a Objetos
- Introducción a la POO
- Clases y Objetos
- Métodos
- Constructores
- Herencia
- Polimorfismo
- Encapsulamiento
- Abstracción
Módulo 4: Programación Orientada a Objetos Avanzada
Módulo 5: Estructuras de Datos y Colecciones
Módulo 6: Manejo de Excepciones
- Introducción a las Excepciones
- Bloque Try-Catch
- Throw y Throws
- Excepciones Personalizadas
- Bloque Finally
Módulo 7: Entrada/Salida de Archivos
- Lectura de Archivos
- Escritura de Archivos
- Flujos de Archivos
- BufferedReader y BufferedWriter
- Serialización
Módulo 8: Multihilo y Concurrencia
- Introducción al Multihilo
- Creación de Hilos
- Ciclo de Vida de un Hilo
- Sincronización
- Utilidades de Concurrencia
Módulo 9: Redes
- Introducción a las Redes
- Sockets
- ServerSocket
- DatagramSocket y DatagramPacket
- URL y HttpURLConnection
