¿Qué librerías de Java son siempre importadas?

De JAR a Módulo: La Evolución en Java

10/12/2022

Valoración: 4.47 (2408 votos)

En el vasto universo del desarrollo de software, la capacidad de reutilizar código es una piedra angular que impulsa la eficiencia y la calidad. Java, como uno de los lenguajes más influyentes, ha evolucionado significativamente en su enfoque para gestionar y organizar el código reutilizable. Durante años, las librerías en formato JAR han sido el estándar, permitiéndonos integrar funcionalidades complejas con facilidad. Sin embargo, con la llegada de Java 9 y el Proyecto Jigsaw, la arquitectura modular emergió como una respuesta a los desafíos inherentes de las librerías tradicionales, prometiendo un futuro de aplicaciones más robustas, seguras y escalables. Este artículo te guiará a través de la transición de las librerías a los módulos, explicando sus diferencias, su utilidad y cómo puedes aprovechar esta poderosa evolución en tus propios proyectos.

¿Qué contienen las librerías jep.jar?
En pocas palabras estas librerías contienen códigos que nos facilitan en la elaboración del Proyecto. Para este pequeño Proyecto utilizaremos la librería JEP.jar en el Entorno de Desarrollo NetBeans Utilizaremos la versión: jep-java-3.4-trial.jar Descargar
Índice de Contenido

¿Qué es una Librería en Java? El Punto de Partida

Antes de sumergirnos en el mundo de la modularidad, es fundamental comprender qué son las librerías y por qué fueron, durante mucho tiempo, la única opción para la reutilización de código en Java. Una librería en Java es, en esencia, un conjunto de clases compiladas y empaquetadas en un único fichero, comúnmente con la extensión .jar (Java Archive). Estos archivos JAR contienen el bytecode de las clases, recursos y metadatos necesarios para que funcionen como una unidad cohesiva. La principal ventaja de las librerías radica en su capacidad para añadir funcionalidad a nuestro proyecto sin la necesidad de escribir el código desde cero. Por ejemplo, si nuestra aplicación necesita generar informes en formato PDF, en lugar de implementar toda la lógica de creación de PDF, podemos simplemente integrar una librería como Apache FOP. Esto acelera el desarrollo, reduce errores y nos permite apoyarnos en el trabajo ya realizado y probado por otros desarrolladores.

Sin embargo, las librerías también presentaban desafíos significativos, especialmente en proyectos grandes con numerosas dependencias. El principal problema era el conocido "JAR hell" (infierno de JARs), una situación donde los conflictos de versiones entre diferentes librerías se volvían difíciles de manejar. Las dependencias no eran explícitas, y el classpath (la ruta donde la JVM busca las clases) podía volverse un laberinto, llevando a errores en tiempo de ejecución como ClassNotFoundException o NoClassDefFoundError. La encapsulación era débil, permitiendo el acceso a clases internas que no estaban destinadas a ser parte de la API pública de una librería.

El Salto a la Modularidad: ¿Qué es un Módulo y para qué se Utiliza?

Con Java 9, la introducción del Sistema de Módulos de la Plataforma Java (JPMS), también conocido como Project Jigsaw, marcó un cambio fundamental en cómo Java maneja la organización y la gestión de código. Un módulo va más allá de ser un simple contenedor de clases; es una unidad de encapsulación más robusta y explícita. Mientras que una librería solo agrupa clases, un módulo declara explícitamente qué paquetes exporta (haciéndolos accesibles a otros módulos) y qué otros módulos requiere para funcionar. Esta declaración explícita es la clave de su poder.

La arquitectura modular ofrece varias ventajas cruciales:

  • Encapsulación Fuerte: Los módulos permiten una encapsulación más estricta. Solo los paquetes explícitamente exportados son visibles para otros módulos. Esto significa que las clases internas de un módulo están verdaderamente ocultas, lo que mejora la mantenibilidad y previene el uso indebido de APIs internas.
  • Dependencias Claras: Al declarar explícitamente sus dependencias, los módulos hacen que la estructura de la aplicación sea mucho más clara y predecible. Se elimina gran parte del "JAR hell" al permitir que el sistema de módulos resuelva conflictos y asegure que todas las dependencias necesarias estén presentes.
  • Fiabilidad: El sistema de módulos verifica las dependencias en tiempo de compilación y en tiempo de ejecución, lo que reduce la probabilidad de errores relacionados con clases faltantes o conflictos de versiones.
  • Menor Tamaño del Runtime: La modularidad permite crear runtimes de Java personalizados que incluyen solo los módulos de la plataforma que son estrictamente necesarios para la aplicación, reduciendo significativamente el tamaño de la distribución de Java y mejorando el rendimiento de arranque.

La Conversión: ¿Cómo Convertir una Librería en Módulo?

La esencia de convertir una librería existente en un módulo radica en la creación de un descriptor de módulo. Este descriptor es un archivo especial llamado module-info.java. Este archivo se coloca en el directorio raíz del módulo y contiene las declaraciones sobre lo que el módulo exporta y lo que requiere.

El Archivo module-info.java

Este fichero descriptor es el corazón de la modularidad. Nos permite definir el comportamiento de nuestro módulo de la siguiente manera:

Exportación de Paquetes (exports)

Para que las clases de un paquete sean accesibles desde otros módulos, deben ser explícitamente exportadas. Si un paquete no se exporta, sus clases solo serán accesibles dentro del propio módulo, lo que garantiza una encapsulación fuerte.

Ejemplo de module-info.java para exportación:

module EjemploModulo {
exports es.stack.ejemploStack1;
}

El código anterior define el módulo EjemploModulo donde se exportan las clases ubicadas en el paquete es.stack.ejemploStack1. Esto significa que cualquier otro módulo que requieraEjemploModulo podrá usar las clases públicas de es.stack.ejemploStack1.

¿Qué es la Clase Math en Java?
La Clase Math en Java es esencialmente una biblioteca de funciones matemáticas y constantes. Esta clase nos facilita realizar cálculos y operaciones matemáticas comunes sin tener que implementarlos desde cero.

Importación/Requerimiento de Módulos (requires)

Si nuestro módulo necesita utilizar funcionalidades de otro módulo, debe declararlo explícitamente mediante la palabra clave requires.

Ejemplo de module-info.java para requerimientos:

module NuevoEjemplo {
requires EjemploModulo;
}

En este caso, el módulo NuevoEjemplo indica que necesita el módulo EjemploModulo para funcionar. Esto permite al sistema de módulos verificar que EjemploModulo esté presente y sea compatible.

Módulos Automáticos: La Transición Suave

Una característica clave para facilitar la transición de librerías a módulos es el concepto de módulos automáticos. Son librerías JAR que, aunque no contienen un archivo module-info.java explícito, se tratan como módulos cuando se colocan en el module-path (la ruta donde se buscan los módulos en tiempo de compilación o ejecución). Cuando una librería se convierte en un módulo automático:

  • Se le asigna un nombre de módulo derivado del nombre del archivo JAR (sin la extensión .jar).
  • Exporta automáticamente todos sus paquetes con todas sus clases. Esto significa que la encapsulación fuerte de los módulos explícitos no se aplica a los módulos automáticos.
  • Puede ser requerido por módulos explícitos.

Los módulos automáticos son una solución de compatibilidad que permite que las aplicaciones modulares utilicen librerías antiguas sin necesidad de que estas sean reempaquetadas como módulos explícitos. Sin embargo, se recomienda que, con el tiempo, las librerías se conviertan en módulos explícitos para aprovechar al máximo los beneficios de la modularidad.

Class Path vs. Module Path

Con Java 9, coexisten dos rutas importantes para la resolución de clases y módulos:

  • Class Path (forma clásica): Es la ruta donde la JVM busca las clases en tiempo de compilación o ejecución. Apunta a librerías .jar o directorios que contienen clases compiladas. Las librerías en el classpath se comportan como siempre lo han hecho, sin las ventajas de la modularidad.
  • Module Path: Es la ruta donde la JVM buscará los módulos (explícitos o automáticos) en tiempo de compilación o ejecución. Si un JAR se coloca en el module-path, se tratará como un módulo (automático si no tiene module-info.java, o explícito si lo tiene).

Es crucial entender la diferencia, ya que el comportamiento de un JAR cambia dependiendo de si se carga desde el classpath o el module-path.

¿Cómo Importar una Librería en Java?

El concepto de "importar una librería" en Java tiene dos significados principales que a menudo se confunden, pero son distintos:

  1. Añadir la Librería al Proyecto (Configuración del Entorno): Esto se refiere a hacer que el archivo JAR de la librería esté disponible para el compilador y la JVM. Antes de Java 9, esto se hacía colocando el JAR en el classpath de tu proyecto. En entornos de desarrollo modernos (IDE como Eclipse, IntelliJ IDEA, NetBeans) o con herramientas de construcción (Maven, Gradle), se configura como una dependencia del proyecto. Con Java 9+, también puedes colocarla en el module-path si quieres que se trate como un módulo.
  2. Usar Clases de la Librería en tu Código (Declaración import): Una vez que la librería está disponible para tu proyecto, debes indicar en tu código fuente qué clases específicas de esa librería deseas usar. Esto se hace con la palabra clave import.

La Declaración import en Java

La declaración import en Java no "trae" la librería completa a tu archivo, sino que simplemente proporciona una forma abreviada de referenciar las clases. Sin un import, tendrías que usar el nombre completo de la clase, incluyendo su paquete (por ejemplo, java.util.ArrayList). Con import java.util.ArrayList;, puedes simplemente usar ArrayList.

Sintaxis para import en Java

La declaración import siempre debe ir después de la declaración del paquete de tu clase (si la hay) y antes de la declaración de la clase misma.

¿Cómo añadir librerías propias de Java?
Seguidamente nos aparecerá una ventana emergente donde deberemos dirigirnos a la pestaña Libraries. En esta ventana podremos observar que actualmente se encuentran las librerías propias de java (el JRE). Ya solo nos quedaría clickar en el botón Add External Jars… añadir la o las librerías necesarias y pulsar en aceptar.
  • Importar una Clase Específica: Esta es la forma más recomendada, ya que solo importa lo que realmente necesitas.
package com.mi_aplicacion;

import java.util.ArrayList;

public class MiClase {
// ... uso de ArrayList
}
  • Importar Todas las Clases de un Paquete: Puedes usar el asterisco (*) para importar todas las clases públicas de un paquete. Si bien es conveniente, puede llevar a ambigüedades si tienes clases con el mismo nombre en diferentes paquetes y, teóricamente, puede hacer que el compilador tenga que buscar en más lugares, aunque en la práctica el impacto en el rendimiento de compilación es mínimo. Sin embargo, no importa subpaquetes.
package com.mi_aplicacion;

import java.util.*; // Importa todas las clases de java.util

public class OtraClase {
// ... uso de ArrayList, HashMap, etc.
}

Importando Clases Propias de Java (API Estándar)

Java viene con una vasta Biblioteca de Clases Estándar (API de Java). Algunas librerías fundamentales, como java.lang (que contiene clases como String, System, Object), se importan automáticamente y no requieren una declaración import explícita. Para el resto, debes importarlas.

Ejemplo:

import java.io.File;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

public class EjemploApiJava {
// ...
}

La documentación oficial de la API de Java es tu mejor amiga para conocer todas las clases disponibles, los paquetes a los que pertenecen y cómo usarlas.

Tabla Comparativa: Librería (JAR) vs. Módulo

Para resumir las diferencias clave entre las librerías tradicionales y los módulos, veamos la siguiente tabla:

CaracterísticaLibrería (JAR) ClásicaMódulo (JPMS)
FormatoArchivo .jarArchivo .jar (con module-info.java)
DescriptorNo tiene un descriptor explícito de dependencias/exports.Archivo module-info.java que declara dependencias y exports.
EncapsulaciónDébil; todas las clases públicas son accesibles.Fuerte; solo los paquetes explícitamente exportados son accesibles.
Declaración de DependenciasImplícita; gestionada por el classpath o herramientas de construcción.Explícita (requires); verificada por el sistema de módulos.
Ruta de CargaClass PathModule Path
Resolución de ConflictosPropenso a "JAR hell" (colisiones de clases, versiones).El sistema de módulos ayuda a detectar y prevenir conflictos.
Optimización de RuntimeNo permite optimización del JRE.Permite crear runtimes personalizados y más pequeños.
CompatibilidadUniversal en todas las versiones de Java.Introducido en Java 9, compatible hacia atrás mediante módulos automáticos.

Preguntas Frecuentes sobre Librerías y Módulos en Java

¿Pueden coexistir librerías y módulos en un mismo proyecto?

Sí, absolutamente. Java 9 fue diseñado con la compatibilidad hacia atrás en mente. Las librerías tradicionales (JARs sin module-info.java) pueden coexistir con módulos explícitos. Cuando una librería se coloca en el module-path, se convierte en un módulo automático, lo que permite que los módulos explícitos la "requieran" y utilicen sus funcionalidades. Esto facilita una migración gradual a la modularidad sin la necesidad de reescribir todo el código existente de inmediato.

¿Debo convertir todas mis librerías existentes a módulos explícitos?

No necesariamente de inmediato. Si tienes librerías antiguas que funcionan bien como módulos automáticos, no hay una necesidad urgente de convertirlas. Sin embargo, para nuevas librerías o para refactorizar partes críticas de una aplicación, convertir a módulos explícitos es altamente recomendable. Esto te permitirá aprovechar la encapsulación fuerte, las dependencias claras y los beneficios de rendimiento y fiabilidad que ofrece la arquitectura modular.

¿Qué es el "JAR hell" y cómo lo resuelve la modularidad?

El "JAR hell" es un término que describe los problemas que surgen en aplicaciones Java con muchas dependencias JAR. Estos problemas incluyen:

  • Colisiones de Nombres: Diferentes JARs contienen clases con el mismo nombre de paquete y clase.
  • Problemas de Versión: Diferentes JARs requieren versiones incompatibles de la misma librería.
  • Dependencias Implícitas: Un JAR puede depender de otro que no está presente en el classpath, llevando a errores en tiempo de ejecución.

La modularidad aborda estos problemas al:

  • Encapsular Clases: Al exportar solo los paquetes públicos, las clases internas se ocultan, reduciendo las colisiones de nombres.
  • Declarar Dependencias Explícitas: El sistema de módulos verifica que todas las dependencias requeridas estén presentes y sean compatibles, detectando problemas mucho antes en el ciclo de desarrollo.
  • Aislar Módulos: Permite que diferentes versiones de un mismo módulo coexistan en ciertos escenarios avanzados, aunque es una característica más compleja.

¿Dónde puedo encontrar la documentación de los módulos y librerías de Java?

La fuente principal para toda la información sobre las clases, paquetes y módulos de Java es la Documentación Oficial de la API de Java. Puedes encontrarla en el sitio web de Oracle. Esta documentación es exhaustiva y detalla cada clase, método, interfaz y paquete, así como la estructura modular de la plataforma Java.

Conclusión

La evolución de Java, desde la gestión de dependencias basada en librerías JAR hasta la adopción de un sistema de módulos robusto, representa un paso significativo hacia la construcción de aplicaciones más eficientes, seguras y mantenibles. Comprender las diferencias entre una librería y un módulo, así como dominar el proceso de importación y las ventajas de la modularidad, es esencial para cualquier desarrollador Java moderno. Si bien las librerías seguirán siendo una parte integral del ecosistema, la arquitectura modular es el camino a seguir para proyectos que buscan optimizar el rendimiento, mejorar la fiabilidad y simplificar la gestión de dependencias. Al adoptar la modularidad, no solo estamos actualizando nuestras habilidades, sino que estamos construyendo un futuro más sólido para nuestras aplicaciones Java.

Si quieres conocer otros artículos parecidos a De JAR a Módulo: La Evolución en Java puedes visitar la categoría Librerías.

Subir