Java 8: Guía de Referencia Rápida Completa
Java 8 - Visión General
Java 8 es una versión mayor del lenguaje de programación Java, lanzada inicialmente el 18 de marzo de 2014. Con esta versión, Java añade soporte para programación funcional, nuevo motor JavaScript, nuevas APIs para manipulación de fecha/hora, nueva API de Streams, entre otras mejoras.
Nuevas Características Principales
Expresiones Lambda — Añaden capacidad de procesamiento funcional a Java.
Referencias a Métodos — Referenciar funciones por nombre en lugar de invocarlas directamente; usar funciones como parámetros.
Métodos Default — Interfaces con implementación por defecto.
Nuevas Herramientas — Herramientas de compilación y utilidades como
jdepspara analizar dependencias.API Stream — Nueva API para facilitar procesamiento en tubería (pipeline).
API Date/Time — API de fecha/hora mejorada (
java.time).Optional — Énfasis en buenas prácticas para manejar valores nulos correctamente.
Nashorn, Motor JavaScript — Motor basado en Java para ejecutar código JavaScript.
Ejemplo Comparativo: Java 7 vs Java 8
Considera el siguiente fragmento de código que ordena una lista de nombres.
Demo en vivoimport java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
public class Java8Tester {
public static void main(String args[]) {
List<String> names1 = new ArrayList<>();
names1.add("Mahesh ");
names1.add("Suresh ");
names1.add("Ramesh ");
names1.add("Naresh ");
names1.add("Kalpesh ");
List<String> names2 = new ArrayList<>();
names2.add("Mahesh ");
names2.add("Suresh ");
names2.add("Ramesh ");
names2.add("Naresh ");
names2.add("Kalpesh ");
Java8Tester tester = new Java8Tester();
System.out.println("Ordenación con sintaxis Java 7: ");
tester.sortUsingJava7(names1);
System.out.println(names1);
System.out.println("Ordenación con sintaxis Java 8: ");
tester.sortUsingJava8(names2);
System.out.println(names2);
}
// Ordenación usando Java 7 (clase anónima)
private void sortUsingJava7(List<String> names) {
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
}
// Ordenación usando Java 8 (expresión lambda)
private void sortUsingJava8(List<String> names) {
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
}
}
Ejecuta el programa para obtener:
Ordenación con sintaxis Java 7: [ Kalpesh Mahesh Naresh Ramesh Suresh ] Ordenación con sintaxis Java 8: [ Kalpesh Mahesh Naresh Ramesh Suresh ]
El método sortUsingJava8() usa sort con una expresión lambda como parámetro para definir el criterio de ordenación.
Java 8 - Configuración del Entorno
Configuración Local
Si deseas configurar tu propio entorno para Java, esta sección te guía en el proceso.
Java SE se puede descargar gratis desde:
https://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html
Descarga la versión según tu sistema operativo. Ejecuta el .exe para instalar. Tras la instalación, configura las variables de entorno para apuntar a los directorios de instalación correctos.
Configurar PATH en Windows 2000/XP/7/10/11
Suponiendo que instalaste Java en c:\Program Files\java\jdk:
- Clic derecho en "Este equipo" → "Propiedades".
- Clic en "Variables de entorno" bajo la pestaña "Opciones avanzadas".
- Modifica la variable "Path" para incluir la ruta al ejecutable de Java. Ejemplo:
C:\WINDOWS\SYSTEM32;c:\Program Files\java\jdk\bin.
Configurar PATH en Linux, UNIX, Solaris, FreeBSD
La variable de entorno PATH debe apuntar a donde están los binarios de Java. Consulta la documentación de tu shell si tienes dificultades.
Ejemplo para bash: añade al final de .bashrc:
export PATH=/path/to/java:$PATH
Editores Java Populares
Notepad — Editor simple en Windows (recomendado para este tutorial) o TextPad.
NetBeans — IDE Java open-source y gratuito. https://netbeans.org/
Eclipse — IDE Java de la comunidad open-source Eclipse. https://www.eclipse.org/
IntelliJ IDEA — IDE potente con versión Community gratuita. https://www.jetbrains.com/idea/
VS Code — Editor ligero con extensiones para Java. https://code.visualstudio.com/
Java 8 - Expresiones Lambda
Las expresiones lambda, introducidas en Java 8, son consideradas la característica más importante. Facilitan la programación funcional y simplifican enormemente el desarrollo.
Sintaxis
parámetro -> cuerpo de la expresión
Características Importantes
Declaración de tipo opcional — No es necesario declarar el tipo del parámetro; el compilador lo infiere.
Paréntesis opcionales — No se requieren paréntesis para un solo parámetro; obligatorios para múltiples.
Llaves opcionales — No se necesitan llaves si el cuerpo tiene una sola instrucción.
Palabra clave return opcional — El compilador devuelve automáticamente el valor si el cuerpo es una sola expresión. Las llaves indican que se retorna un valor.
Ejemplo de Expresiones Lambda
Crea el siguiente programa en C:\JAVA.
Java8Tester.java
Demo en vivopublic class Java8Tester {
public static void main(String args[]) {
Java8Tester tester = new Java8Tester();
// Con declaración de tipo
MathOperation addition = (int a, int b) -> a + b;
// Sin declaración de tipo
MathOperation subtraction = (a, b) -> a - b;
// Con return y llaves
MathOperation multiplication = (int a, int b) -> { return a * b; };
// Sin return ni llaves
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// Sin paréntesis
GreetingService greetService1 = message -> System.out.println("Hola " + message);
// Con paréntesis
GreetingService greetService2 = (message) -> System.out.println("Hola " + message);
greetService1.sayMessage("Mahesh");
greetService2.sayMessage("Suresh");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation) {
return mathOperation.operation(a, b);
}
}
Verificar el Resultado
C:\JAVA>javac Java8Tester.java C:\JAVA>java Java8Tester
10 + 5 = 15 10 - 5 = 5 10 x 5 = 50 10 / 5 = 2 Hola Mahesh Hola Suresh
Puntos clave del ejemplo:
- Las lambdas definen implementaciones inline de interfaces funcionales (una sola método abstracto).
- Eliminan la necesidad de clases anónimas, dando capacidad de programación funcional simple y potente.
Ámbito (Scope)
Una lambda puede referirse a variables final o effectively final (asignadas una sola vez). Error de compilación si una variable se asigna por segunda vez.
Ejemplo de Ámbito
Java8Tester.java
Demo en vivopublic class Java8Tester {
final static String salutation = "¡Hola! ";
public static void main(String args[]) {
GreetingService greetService1 = message -> System.out.println(salutation + message);
greetService1.sayMessage("Mahesh");
}
interface GreetingService {
void sayMessage(String message);
}
}
Verificar el Resultado
¡Hola! Mahesh
Java 8 - Referencias a Métodos
Las referencias a métodos permiten apuntar a métodos por su nombre usando el símbolo ::. Pueden referenciar:
- Métodos estáticos
- Métodos de instancia
- Constructores con operador new (
TreeSet::new)
Ejemplo de Referencia a Método
Crea el siguiente programa en C:\JAVA.
Java8Tester.java
import java.util.List;
import java.util.ArrayList;
public class Java8Tester {
public static void main(String args[]) {
List<String> names = new ArrayList<>();
names.add("Mahesh");
names.add("Suresh");
names.add("Ramesh");
names.add("Naresh");
names.add("Kalpesh");
names.forEach(System.out::println);
}
}
Aquí pasamos System.out::println como referencia a método estático.
Verificar el Resultado
C:\JAVA>javac Java8Tester.java C:\JAVA>java Java8Tester
Mahesh Suresh Ramesh Naresh Kalpesh
Java 8 - Interfaces Funcionales
Las interfaces funcionales tienen una única funcionalidad. Ejemplo: Comparable con compareTo. Java 8 define muchas interfaces funcionales en java.util.function para uso extensivo en lambdas.
Lista de interfaces funcionales en Java 8 (clic para expandir)
| Nº | Interfaz y Descripción |
|---|---|
| 1 | BiConsumer<T,U> Operación que acepta dos argumentos y no devuelve resultado. |
| 2 | BiFunction<T,U,R> Función que acepta dos argumentos y produce un resultado. |
| 3 | BinaryOperator<T> Operación sobre dos operandos del mismo tipo, produce resultado del mismo tipo. |
| 4 | BiPredicate<T,U> Predicado (función booleana) de dos argumentos. |
| 5 | BooleanSupplier Proveedor de resultados booleanos. |
| 6 | Consumer<T> Operación que acepta un argumento y no devuelve resultado. |
| 7 | DoubleBinaryOperator Operación sobre dos operandos double, produce double. |
| 8 | DoubleConsumer Operación que acepta un argumento double y no devuelve resultado. |
| 9 | DoubleFunction<R> Función que acepta double y produce resultado. |
| 10 | DoublePredicate Predicado de un argumento double. |
| 11 | DoubleSupplier Proveedor de resultados double. |
| 12 | DoubleToIntFunction Función que acepta double y produce int. |
| 13 | DoubleToLongFunction Función que acepta double y produce long. |
| 14 | DoubleUnaryOperator Operación sobre un operando double que produce double. |
| 15 | Function<T,R> Función que acepta un argumento y produce resultado. |
| 16 | IntBinaryOperator Operación sobre dos enteros que produce int. |
| 17 | IntConsumer Operación que acepta un int y no devuelve resultado. |
| 18 | IntFunction<R> Función que acepta int y produce resultado. |
| 19 | IntPredicate Predicado de un argumento int. |
| 20 | IntSupplier Proveedor de resultados int. |
| 21 | IntToDoubleFunction Función que acepta int y produce double. |
| 22 | IntToLongFunction Función que acepta int y produce long. |
| 23 | IntUnaryOperator Operación sobre un operando int que produce int. |
| 24 | LongBinaryOperator Operación sobre dos long que produce long. |
| 25 | LongConsumer Operación que acepta un long y no devuelve resultado. |
| 26 | LongFunction<R> Función que acepta long y produce resultado. |
| 27 | LongPredicate Predicado de un argumento long. |
| 28 | LongSupplier Proveedor de resultados long. |
| 29 | LongToDoubleFunction Función que acepta long y produce double. |
| 30 | LongToIntFunction Función que acepta long y produce int. |
| 31 | LongUnaryOperator Operación sobre un operando long que produce long. |
| 32 | ObjDoubleConsumer<T> Operación que acepta objeto y double, no devuelve resultado. |
| 33 | ObjIntConsumer<T> Operación que acepta objeto e int, no devuelve resultado. |
| 34 | ObjLongConsumer<T> Operación que acepta objeto y long, no devuelve resultado. |
| 35 | Predicate<T> Predicado de un argumento. |
| 36 | Supplier<T> Proveedor de resultados. |
| 37 | ToDoubleBiFunction<T,U> Función que acepta dos argumentos y produce double. |
| 38 | ToDoubleFunction<T> Función que produce double. |
| 39 | ToIntBiFunction<T,U> Función que acepta dos argumentos y produce int. |
| 40 | ToIntFunction<T> Función que produce int. |
| 41 | ToLongBiFunction<T,U> Función que acepta dos argumentos y produce long. |
| 42 | ToLongFunction<T> Función que produce long. |
| 43 | UnaryOperator<T> Operación sobre un operando que produce resultado del mismo tipo. |
Ejemplo de Interfaz Funcional
Predicate<T> es una interfaz funcional con método test(Object) que devuelve boolean.
Crea el siguiente programa en C:\JAVA.
Java8Tester.java
Demo en vivoimport java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
public static void main(String args[]) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate que siempre devuelve true
System.out.println("Imprimir todos los números:");
eval(list, n -> true);
// Predicate para números pares
System.out.println("Imprimir números pares:");
eval(list, n -> n % 2 == 0);
// Predicate para números > 3
System.out.println("Imprimir números mayores que 3:");
eval(list, n -> n > 3);
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for (Integer n : list) {
if (predicate.test(n)) {
System.out.print(n + " ");
}
}
System.out.println();
}
}
Verificar el Resultado
C:\JAVA>javac Java8Tester.java C:\JAVA>java Java8Tester
Imprimir todos los números: 1 2 3 4 5 6 7 8 9 Imprimir números pares: 2 4 6 8 Imprimir números mayores que 3: 4 5 6 7 8 9
Java 8 - Métodos Default
Java 8 introduce implementación por defecto en interfaces para compatibilidad hacia atrás, permitiendo que interfaces antiguas aprovechen lambdas.
Ejemplo: List o Collection no tenían forEach. Añadirlo rompería implementaciones existentes. Java 8 permite default para que List/Collection tengan implementación por defecto de forEach, y las clases que las implementan no necesitan implementarlo.
Sintaxis
public interface Vehicle {
default void print() {
System.out.println("¡Soy un vehículo!");
}
}
Múltiples Defaults
Si una clase implementa dos interfaces con el mismo método default, hay ambigüedad. Dos soluciones:
1. Sobrescribir el método:
public interface Vehicle {
default void print() { System.out.println("¡Soy un vehículo!"); }
}
public interface FourWheeler {
default void print() { System.out.println("¡Soy un vehículo de cuatro ruedas!"); }
}
public class Car implements Vehicle, FourWheeler {
public void print() {
System.out.println("¡Soy un coche de cuatro ruedas!");
}
}
2. Invocar el default de la interfaz específica con super:
public class Car implements Vehicle, FourWheeler {
public void print() {
Vehicle.super.print();
}
}
Métodos Default Estáticos
Las interfaces pueden tener métodos estáticos auxiliares desde Java 8.
public interface Vehicle {
default void print() { System.out.println("¡Soy un vehículo!"); }
static void blowHorn() { System.out.println("¡Sonando claxon!!!"); }
}
Ejemplo de Método Default
Crea el siguiente programa en C:\JAVA.
Java8Tester.java
Demo en vivopublic class Java8Tester {
public static void main(String args[]) {
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print() { System.out.println("¡Soy un vehículo!"); }
static void blowHorn() { System.out.println("¡Sonando claxon!!!"); }
}
interface FourWheeler {
default void print() { System.out.println("¡Soy un vehículo de cuatro ruedas!"); }
}
class Car implements Vehicle, FourWheeler {
public void print() {
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("¡Soy un coche!");
}
}
Verificar el Resultado
C:\JAVA>javac Java8Tester.java C:\JAVA>java Java8Tester
¡Soy un vehículo! ¡Soy un vehículo de cuatro ruedas! ¡Sonando claxon!!! ¡Soy un coche!
Java 8 - Streams
Stream es una capa abstracta nueva en Java 8. Permite procesar datos de forma declarativa, similar a SQL.
Ejemplo SQL:
SELECT max(salary), employee_id, employee_name FROM Employee
SQL devuelve automáticamente el empleado con mayor salario sin cálculo explícito. Con Collections en Java, el desarrollador usa bucles y comprobaciones repetidas. Además, con procesadores multi-núcleo, escribir código paralelo es propenso a errores.
Java 8 introduce Stream para procesar datos declarativamente y aprovechar arquitectura multi-núcleo sin código específico.
¿Qué es un Stream?
Representa una secuencia de objetos desde una fuente, soportando operaciones agregadas.
Secuencia de elementos — Proporciona elementos de tipo específico de forma secuencial. Calcula elementos bajo demanda; nunca los almacena.
Fuente — Colecciones, Arrays o recursos I/O.
Operaciones agregadas — filter, map, limit, reduce, find, match, etc.
Pipelining — La mayoría de operaciones devuelven stream para encadenar. Son operaciones intermedias.
collect()es operación terminal al final.Iteración automática — Las operaciones iteran internamente, a diferencia de Collections donde se requiere iteración explícita.
Generar Streams
La interfaz Collection tiene dos métodos:
stream() — Stream secuencial.
parallelStream() — Stream paralelo.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toList());
forEach
Nuevo método para iterar cada elemento del stream.
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
map
Mapea cada elemento a su resultado correspondiente.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = numbers.stream()
.map(i -> i * i)
.distinct()
.collect(Collectors.toList());
filter
Elimina elementos según criterio.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.stream().filter(string -> string.isEmpty()).count();
limit
Reduce el tamaño del stream.
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
sorted
Ordena el stream.
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
Procesamiento Paralelo
parallelStream es la alternativa para procesamiento paralelo. Fácil cambio entre secuencial y paralelo.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.parallelStream()
.filter(string -> string.isEmpty())
.count();
Collectors
Combinan resultados del procesamiento de elementos del stream. Pueden devolver lista o cadena.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toList());
System.out.println("Lista filtrada: " + filtered);
String mergedString = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.joining(", "));
System.out.println("Cadena combinada: " + mergedString);
Estadísticas
Collectors de estadísticas para calcular todas las estadísticas durante el procesamiento de stream.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream()
.mapToInt(x -> x)
.summaryStatistics();
System.out.println("Número mayor: " + stats.getMax());
System.out.println("Número menor: " + stats.getMin());
System.out.println("Suma total: " + stats.getSum());
System.out.println("Promedio: " + stats.getAverage());
Ejemplo Completo de Streams
Crea el siguiente programa en C:\JAVA.
Java8Tester.java
Demo en vivoimport java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class Java8Tester {
public static void main(String args[]) {
System.out.println("Usando Java 7: ");
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("Lista: " + strings);
long count = getCountEmptyStringUsingJava7(strings);
System.out.println("Cadenas vacías: " + count);
count = getCountLength3UsingJava7(strings);
System.out.println("Cadenas de longitud 3: " + count);
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("Lista filtrada: " + filtered);
String mergedString = getMergedStringUsingJava7(strings, ", ");
System.out.println("Cadena combinada: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = getSquares(numbers);
System.out.println("Lista de cuadrados: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
System.out.println("Lista: " + integers);
System.out.println("Máximo: " + getMax(integers));
System.out.println("Mínimo: " + getMin(integers));
System.out.println("Suma: " + getSum(integers));
System.out.println("Promedio: " + getAverage(integers));
System.out.println("Números aleatorios: ");
Random random = new Random();
for (int i = 0; i < 10; i++) {
System.out.println(random.nextInt());
}
System.out.println("\nUsando Java 8: ");
System.out.println("Lista: " + strings);
count = strings.stream().filter(s -> s.isEmpty()).count();
System.out.println("Cadenas vacías: " + count);
count = strings.stream().filter(s -> s.length() == 3).count();
System.out.println("Cadenas de longitud 3: " + count);
filtered = strings.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
System.out.println("Lista filtrada: " + filtered);
mergedString = strings.stream().filter(s -> !s.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Cadena combinada: " + mergedString);
squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());
System.out.println("Lista de cuadrados: " + squaresList);
System.out.println("Lista: " + integers);
IntSummaryStatistics stats = integers.stream().mapToInt(x -> x).summaryStatistics();
System.out.println("Máximo: " + stats.getMax());
System.out.println("Mínimo: " + stats.getMin());
System.out.println("Suma: " + stats.getSum());
System.out.println("Promedio: " + stats.getAverage());
System.out.println("Números aleatorios: ");
random.ints().limit(10).sorted().forEach(System.out::println);
// Procesamiento paralelo
count = strings.parallelStream().filter(s -> s.isEmpty()).count();
System.out.println("Cadenas vacías (paralelo): " + count);
}
// Métodos Java 7 (omitidos por brevedad, igual que original)
private static int getCountEmptyStringUsingJava7(List<String> strings) { /* ... */ return 0; }
private static int getCountLength3UsingJava7(List<String> strings) { /* ... */ return 0; }
private static List<String> deleteEmptyStringsUsingJava7(List<String> strings) { /* ... */ return null; }
private static String getMergedStringUsingJava7(List<String> strings, String separator) { /* ... */ return ""; }
private static List<Integer> getSquares(List<Integer> numbers) { /* ... */ return null; }
private static int getMax(List<Integer> numbers) { /* ... */ return 0; }
private static int getMin(List<Integer> numbers) { /* ... */ return 0; }
private static int getSum(List<Integer> numbers) { /* ... */ return 0; }
private static int getAverage(List<Integer> numbers) { /* ... */ return 0; }
}
Verificar el Resultado
C:\JAVA>javac Java8Tester.java C:\JAVA>java Java8Tester
La salida muestra la equivalencia entre Java 7 (imperativo) y Java 8 (declarativo con Streams).
Java 8 - Clase Optional
Ver artículo dedicado: Dominando Optional en Java 8
Java 8 - Nashorn JavaScript
Ver artículo dedicado: Motor JavaScript Nashorn en Java 8
Java 8 - Nueva API Date/Time
Ver artículo dedicado: Java 8: La Nueva y Robusta API de Fecha y Hora
Java 8 - Base64
Ver artículo dedicado: Base64 en Java 8: Codificación Simple, URL y MIME Simplificada
Java
- Dominando el Mantenimiento Preventivo y Basado en Condición: Guía Práctica de Desarrollo de MP
- Guía experta de máquinas curvadoras de tubos cuadrados
- Prensas en Frío: Guía Completa para Conformado Metálico Eficiente
- Caja de cambios planetaria explicada: reducción de velocidad compacta y de alto par
- Fresadora-taladro Jet: la guía profesional definitiva
- Prensas servo: funcionamiento y ventajas frente a prensas mecánicas
- Guía profesional de cilindros neumáticos: rápidos y fiables
- Desbobinador de Engranajes: Beneficios, Funciones y Operaciones
- Proteja su cadena de suministro: Estrategias integrales de seguros
- Fundamentos de fresado CNC: guía rápida de referencia para operadores profesionales
- Entendiendo Java Vector: Uso, Características y Comparación con ArrayList