Java es el lenguaje de referencia del backend empresarial español y europeo. Sostiene plataformas críticas en banca (core bancario, posicionamiento global, medios de pago), fintech (procesadores de tarjetas, pasarelas, antifraude), telco (OSS/BSS, facturación), logística (WMS, trazabilidad, integración EDI), ecommerce (catálogo, checkout, inventario) y sector público (sedes electrónicas, interoperabilidad). Se mantiene por Oracle y el ecosistema OpenJDK con un ciclo de versiones LTS cada dos años: la versión de referencia en 2026 es Java 25 LTS, combinada con Maven 3.9 o Gradle 8 como herramientas de construcción. Este itinerario lleva al alumno desde la sintaxis básica hasta patrones modernos como records, sealed classes, pattern matching y virtual threads, aplicados en arquitecturas de microservicios y APIs REST.
Qué es Java
Java es a la vez un lenguaje de alto nivel y una plataforma con su propio compilador (javac), máquina virtual (JVM) y librería estándar. Las clases se compilan a bytecode (archivos .class) que cualquier JVM compatible puede ejecutar, haciendo posible el principio de escribir una vez y ejecutar en cualquier lugar.
Arquitectura de la JVM
La JVM es el componente que carga las clases, gestiona la memoria y ejecuta el bytecode. Internamente combina un cargador de clases, áreas de memoria especializadas (heap, stack por hilo, metaspace), un compilador JIT (Just-In-Time) que optimiza código caliente y un recolector de basura que libera memoria de objetos inalcanzables.
flowchart TB
subgraph ClassLoaders [Cargadores de clases]
BCL[Bootstrap ClassLoader]
PCL[Platform ClassLoader]
ACL[Application ClassLoader]
end
subgraph Memoria [Áreas de memoria]
Heap[Heap: objetos y arrays]
Meta[Metaspace: metadatos de clases]
Stack[Stack por hilo: frames de métodos]
PC[Program Counter]
end
subgraph Ejecucion [Motor de ejecución]
Int[Interprete de bytecode]
JIT[Compilador JIT C1 y C2]
GC[Recolector de basura ZGC G1]
end
ClassLoaders --> Memoria
Memoria --> Ejecucion
Ejecucion --> OS[Sistema operativo]
Del código fuente al bytecode
Los archivos .java se compilan con javac a archivos .class que contienen bytecode portable. La JVM interpreta ese bytecode al arrancar y, cuando detecta que un método se ejecuta muchas veces, lo optimiza a código maquina nativo mediante el compilador JIT. GraalVM Native Image permite además producir binarios nativos con arranque casi instantáneo, útil para funciones serverless y contenedores.
flowchart LR
Src[Código fuente .java] -->|javac| Byte[Bytecode .class]
Byte -->|java| JVM[JVM]
JVM -->|interpreta| Exec[Ejecución inicial]
JVM -->|código caliente| JIT[Compilador JIT]
JIT -->|código nativo| CPU[CPU]
Byte -.->|native-image| Native[Binario nativo GraalVM]
Native --> CPU
Caracteristicas principales de Java
- Orientado a objetos: la unidad básica de organización son clases, interfaces y records.
- Portable: el bytecode se ejecuta igual en cualquier sistema operativo con JVM compatible.
- Gestión automática de memoria: el recolector de basura libera objetos inalcanzables.
- Tipado fuerte y estático: la mayoria de errores de tipos se detectan en compilación.
- Concurrente: la plataforma ofrece hilos del sistema, ExecutorService, virtual threads y structured concurrency.
- Ecosistema maduro: Spring, Jakarta EE, Quarkus, Micronaut y un repositorio enorme de librerias en Maven Central.
Instalación y configuración
Descarga del JDK
Para desarrollar en Java se instala el Java Development Kit (JDK). Las distribuciones mas usadas son Oracle JDK, OpenJDK, Eclipse Temurin, Amazon Corretto y Azul Zulu. Todas implementan la misma especificación.
- Oracle JDK: oracle.com/java/technologies/downloads
- OpenJDK: openjdk.org/install
- Eclipse Temurin: adoptium.net
Configuración del entorno
- Windows: definir
JAVA_HOMEapuntando al directorio del JDK y anadir%JAVA_HOME%\binalPATH. - Linux y macOS: anadir al
.bashrco.zshrc:
export JAVA_HOME=/ruta/al/jdk
export PATH=$JAVA_HOME/bin:$PATH
Sintaxis básica moderna
Un programa Java moderno con Java 25 puede prescindir de ceremonia gracias a las unnamed classes (preview) y al bloque estático main. La forma clásica sigue siendo la mas habitual:
public class HolaMundo {
public static void main(String[] args) {
System.out.println("Hola, mundo");
}
}
Compilar y ejecutar:
javac HolaMundo.java
java HolaMundo
Tipos de datos y variables
Java combina tipos primitivos y tipos de referencia. Desde Java 10, el compilador infiere el tipo local con var:
var edad = 30; // int
var salario = 2500.50; // double
var nombre = "Ana"; // String
var numeros = new int[]{1, 2, 3, 4, 5};
Tipos primitivos: byte, short, int, long, float, double, char, boolean.
Tipos de referencia: String, wrappers (Integer, Long, ...), arrays, clases, records, interfaces.
Para cadenas multilinea se usan los text blocks:
String html = """
<html>
<body>Hola</body>
</html>
""";
Operadores
- Aritmeticos:
+,-,*,/,% - Relacionales:
>,<,==,!=,>=,<= - Lógicos:
&&,||,! - Incremento y decremento:
++,-- - Asignación compuesta:
+=,-=,*=,/=
Estructuras de control
Java 25 potencia los switch expressions y el pattern matching, reduciendo el código boilerplate y eliminando los break clásicos:
String etiqueta = switch (dia) {
case LUNES, MARTES, MIERCOLES, JUEVES, VIERNES -> "Dia laboral";
case SABADO, DOMINGO -> "Fin de semana";
};
Sobre tipos selladlos se usan patterns deconstructivos:
double area = switch (figura) {
case Circulo(double radio) -> Math.PI * radio * radio;
case Rectangulo(double base, double h) -> base * h;
case Triangulo(double base, double h) -> 0.5 * base * h;
};
Bucles disponibles: for, while, do-while y for-each:
for (var numero : List.of(1, 2, 3, 4, 5)) {
System.out.println(numero);
}
Programación orientada a objetos en Java 25
Java ofrece tres mecanismos principales para componer tipos: herencia simple con extends, interfaces con implements y jerarquías selladas con sealed y permits. Los records anaden una cuarta via para datos inmutables.
flowchart TB
Clase[Clase concreta] -->|extends| Super[Clase padre]
Clase -->|implements| I1[Interfaz 1]
Clase -->|implements| I2[Interfaz 2]
Sellada[sealed interface Figura] -->|permits| Circulo[record Círculo]
Sellada -->|permits| Rect[record Rectángulo]
Sellada -->|permits| Tri[record Triángulo]
Super -.->|abstract| Meto1[Métodos abstractos]
Super -.->|concrete| Meto2[Métodos concretos]
I1 -.->|default| MetoD[Métodos default]
Clases y objetos
public class Persona {
private String nombre;
private int edad;
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}
public void saludar() {
System.out.println("Hola, me llamo " + nombre);
}
}
var persona = new Persona("Luis", 25);
persona.saludar();
Records vs clases tradicionales
Los records son clases inmutables con equals, hashCode y toString generados por el compilador. Un record es conceptualmente una tupla con nombre; una clase es un agregado mutable con estado interno protegido.
flowchart LR
subgraph Record [record Punto x, y]
RF1[Campos final implícitos]
RE[equals generado]
RH[hashCode generado]
RT[toString generado]
RC[Constructor canonico]
RA[Accesores x y]
end
subgraph Clase [class PuntoMutable]
CF1[Campos privados]
CG[Getters y setters manuales]
CE[equals y hashCode manuales]
CT[toString manual]
CC[Múltiples constructores]
end
Record -.->|Inmutable, conciso| UseR[DTOs, valores, eventos]
Clase -.->|Mutable, flexible| UseC[Entidades con estado, servicios]
public record Punto(double x, double y) {
public double distancia(Punto otro) {
return Math.hypot(x - otro.x, y - otro.y);
}
}
Sealed classes y pattern matching
public sealed interface Figura permits Circulo, Rectangulo, Triangulo {}
public record Circulo(double radio) implements Figura {}
public record Rectangulo(double base, double altura) implements Figura {}
public record Triangulo(double base, double altura) implements Figura {}
Encapsulación
Con los modificadores private, protected, public se controla la visibilidad de atributos y métodos. El patrón habitual es campos private accedidos mediante métodos públicos validados.
Interfaces modernas
Las interfaces pueden contener métodos default, métodos static y métodos private:
public interface Volador {
void volar();
default void planear() {
System.out.println("Planeando en el aire");
}
}
Generics y type erasure
Los generics permiten crear clases y métodos parametrizados por tipo, ofreciendo seguridad en compilación. Java usa type erasure: los parámetros de tipo se eliminan en tiempo de ejecución, lo que preserva compatibilidad binaria con código anterior a Java 5.
flowchart LR
Src["Código con List<String>"] -->|compilación| Check[Chequeo de tipos]
Check -->|OK| Erase[Type erasure]
Erase -->|bytecode| Raw["Tipo bruto List"]
Raw -->|ejecución| JVM[JVM sin parámetros de tipo]
Src -.->|wildcards| Pecs["? extends T, ? super T, T"]
La regla PECS (Producer Extends, Consumer Super) guía el uso de wildcards:
public static <T> void copiar(List<? super T> destino, List<? extends T> origen) {
for (T valor : origen) {
destino.add(valor);
}
}
Manejo de excepciones
Java separa excepciones comprobadas (checked, heredan de Exception sin pasar por RuntimeException) y no comprobadas (unchecked, heredan de RuntimeException o Error). El bloque try-with-resources libera recursos automáticamente cuando implementan AutoCloseable.
flowchart TB
Thr[Throwable] --> Err[Error]
Thr --> Exc[Exception]
Err --> OOM[OutOfMemoryError]
Err --> SOF[StackOverflowError]
Exc --> Check[Checked - declarar o capturar]
Exc --> RTE[RuntimeException]
Check --> IOE[IOException]
Check --> SQL[SQLException]
RTE --> NPE[NullPointerException]
RTE --> IAE[IllegalArgumentException]
RTE --> CCE[ClassCastException]
Exc -.->|try-with-resources| AC[Recurso AutoCloseable]
try (var reader = Files.newBufferedReader(Path.of("datos.txt"))) {
String linea;
while ((linea = reader.readLine()) != null) {
System.out.println(linea);
}
} catch (IOException e) {
System.err.println("No se pudo leer: " + e.getMessage());
}
Colecciones y Streams
El framework de colecciones proporciona List, Set, Map, Queue, Deque y la nueva SequencedCollection (Java 21+). Las colecciones inmutables se crean con métodos factoria of.
List<String> lenguajes = List.of("Java", "Python", "Go");
Set<Integer> primos = Set.of(2, 3, 5, 7, 11);
Map<String, Integer> edades = Map.of("Ana", 25, "Luis", 30);
La API Stream procesa colecciones de forma declarativa como una canalización (pipeline) con operaciones intermedias perezosas y una operación terminal que dispara el cálculo.
flowchart LR
Fuente[Source: collection, of, generate, iterate] --> Int1[filter]
Int1 --> Int2[map]
Int2 --> Int3[sorted]
Int3 --> Int4[distinct]
Int4 --> Term[Terminal: toList, reduce, collect, forEach]
Term --> Result[Resultado final]
style Fuente fill:#b07219,stroke:#333,color:#fff
style Term fill:#2e7d32,stroke:#333,color:#fff
List<String> nombresLargos = lista.stream()
.filter(nombre -> nombre.length() > 5)
.map(String::toUpperCase)
.sorted()
.toList();
Operaciones recientes como Stream.mapMulti, Stream.toList y Stream.gather reducen código y evitan pasos intermedios.
Entrada y salida con NIO.2
La API java.nio.file reemplaza la antigua java.io.File para nuevo código. Los métodos estáticos de Files son los mas comodos:
Path ruta = Path.of("datos", "salida.txt");
Files.writeString(ruta, "Contenido generado");
String contenido = Files.readString(ruta);
Concurrencia: platform threads y virtual threads
Java ofrece dos modelos de hilos. Los platform threads son hilos del sistema operativo (pesados, algunos miles como máximo por JVM). Los virtual threads introducidos como estables en Java 21 son gestionados por la JVM sobre un número pequeno de platform threads portadores, lo que permite lanzar cientos de miles o millones sin agotar recursos.
flowchart TB
subgraph Platform [Platform threads clásicos]
PT1[Platform thread 1]
PT2[Platform thread 2]
PT3[Platform thread 3]
PT1 -.-> OS1[Kernel thread OS]
PT2 -.-> OS2[Kernel thread OS]
PT3 -.-> OS3[Kernel thread OS]
end
subgraph Virtual [Virtual threads Java 21+]
VT1[VT 1]
VT2[VT 2]
VT3[VT ...]
VTN[VT 1,000,000]
Carrier1[Carrier thread]
Carrier2[Carrier thread]
VT1 -.-> Carrier1
VT2 -.-> Carrier1
VT3 -.-> Carrier2
VTN -.-> Carrier2
Carrier1 -.-> OSK1[Kernel thread OS]
Carrier2 -.-> OSK2[Kernel thread OS]
end
Crear un executor de virtual threads:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1_000_000; i++) {
executor.submit(() -> procesar());
}
}
Para tareas concurrentes relacionadas, structured concurrency (preview) agrupa hilos bajo un único scope, propagando cancelaciones y errores como una unidad lógica. ScopedValue sustituye progresivamente a ThreadLocal con mejor rendimiento en virtual threads.
Biblioteca estándar
- java.lang:
String,Math,Object,Thread,Runtime. - java.útil: colecciones,
Optional,Scanner, utilidades. - java.io y java.nio.file: entrada y salida.
- java.net.http: cliente HTTP moderno con
HttpClient. - java.time:
LocalDate,LocalDateTime,Duration,Period. - java.útil.concurrent:
ExecutorService,CompletableFuture, estructuras concurrentes. - java.sql: acceso a bases de datos via JDBC.
Herramientas de construcción
- Maven 3.9+: gestor de dependencias y ciclo de vida basado en
pom.xml. Convención por encima de configuración. - Gradle 8+: alternativa flexible con DSL en Groovy o Kotlin, muy usado en Android y proyectos grandes.
Herramientas de desarrollo (IDEs)
- IntelliJ IDEA: jetbrains.com/idea - el IDE mas usado en Java profesional.
- Eclipse: eclipse.org - maduro, extensible, open source.
- Visual Studio Code: con la extensión oficial Java de Microsoft.
Frameworks del ecosistema
- Spring Framework y Spring Boot: estándar de facto para backend empresarial.
- Jakarta EE: sucesor de Java EE, base de servidores de aplicaciones.
- Quarkus: microservicios optimizados para GraalVM y Kubernetes.
- Micronaut: framework cloud-native con inyección de dependencias en compilación.
Buenas practicas
- Preferir inmutabilidad: usar records y
finalsiempre que sea posible. - Evitar nulos: usar
Optionalen retornos que puedan no tener valor. - Tipado moderno: usar
varen locales para evitar repetir el tipo,sealedpara modelar jerarquías cerradas y pattern matching en lugar de cadenas deinstanceof. - Try-with-resources: siempre que se trabaje con recursos cerrables.
- Virtual threads para tareas bloqueantes numerosas (IO, llamadas HTTP, consultas a base de datos).
- Tests automatizados: JUnit 6 + AssertJ + Mockito como stack estándar.
Recursos adicionales
- Documentación oficial de Java: docs.oracle.com/en/java/javase
- dev.java: dev.java/learn tutoriales oficiales mantenidos por Oracle.
- OpenJDK: openjdk.org proyectos como Loom, Amber, Valhalla y Panama.