Compilar el código fuente y ejecutar con los comandos javac, java y jar en Java 8 o anteriores

Escrito por picodotdev el .
java planeta-codigo programacion
Comentarios

Java

Aún recuerdo cuando empecé a programar con el lenguaje Java sobre el año 1997 que la compilación y ejecución del código la hacía manualmente con los comandos javac, java y jar en un máquina Intel Pentium a 120 Mhz con tan solo 8 MiB, más tarde 32 MiB, con Windows 95 y Java 1.2, momento en el que ni siquiera había un IDE ni las herramientas de construcción modernas como Gradle, había que descargar manualmente las librerías de dependencias en forma de archivos jar que se requiriesen. Luego con JBuilder como IDE este se encargaba de realizar la compilación y ejecución y no hacía falta utilizar estos comandos directamente.

Ahora con herramientas como Gradle además de compilar y ejecutar el programa incluso las dependencias son descargadas de forma automática de repositorios donde se ubican versionadas incluso de forma transitiva, descargando las dependencias de las dependencias.

Usar estos dos comandos directamente ya no es necesario pero como curiosidad comentaré como es su uso. El comando javac sirve para compilar los archivos de código fuente, dado que los paquetes del código fuente de Java se corresponden con directorios en el sistema de archivos el código fuente se ha de ubicar de forma consistente entre la estructura de directorio y el código fuente. Suponiendo que que hay las siguientes clases que hacen uso de la librería log4j2 y están ubicadas en el directorio src/main/java con la misma convención que utiliza Gradle el comando para realizar la compilación y copiar los recursos es el siguiente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ tree
.
├── jar.sh
├── javac.sh
├── java-jar.sh
├── java.sh
├── libraries
│   ├── log4j-api-2.11.1.jar
│   └── log4j-core-2.11.1.jar
├── src
│   └── main
│       ├── java
│       │   └── io
│       │       └── github
│       │           └── picodotdev
│       │               └── blogbitix
│       │                   └── java8
│       │                       └── helloworld
│       │                           └── Main.java
│       ├── misc
│       │   └── MANIFEST.MF
│       └── resources
│           └── log4j2.xml
└── target
    └── classes

14 directories, 9 files
1
2
3
#!/usr/bin/env bash
javac -classpath "libraries/*" -sourcepath src/main/java -source 1.8 -target 1.8 -d target/classes src/main/java/io/github/picodotdev/blogbitix/java8/helloworld/Main.java
cp -r src/main/resources/* target/classes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package io.github.picodotdev.blogbitix.java8.helloworld;

import java.util.Arrays;
import java.util.stream.Collectors;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Main {

    private static Logger logger = LogManager.getLogger(Main.class);

    public static void main(String[] args) {
        logger.info("Arguments: {}", Arrays.asList(args).stream().collect(Collectors.joining(", ")));
    }
}

Con el parámetro -classpath se indica la ubicación de las librerías o dependencias que requiriere el código fuente, con el parámetro -sourcepath el directorio raíz de los archivos de código fuente, el parámetro -source indica la versión del lenguaje del código fuente, -target la versión de la máquina virtual del bytecode que generará el compilador y con el parámetro -d el directorio donde generan los archivos class con el bytecode.

Una vez generados los archivos de bytecode a partir de la compilación del código fuente su ejecución se realiza con el comando java donde hay que indicar las ubicaciones del los archivos class y las librerías jar necesarias que necesiten, la clase principal con el punto de entrada del programa que contenga un método public static void main(String[] args) y los parámetros del programa que se reciben en el parámetro args del método main.

1
2
#!/usr/bin/env bash
java -classpath "target/classes:libraries/*" io.github.picodotdev.blogbitix.java8.helloworld.Main "$@"
1
2
$ ./java.sh arg1 arg2 arg3
16:13:45.386 [main] INFO  io.github.picodotdev.blogbitix.java8.helloworld.Main Arguments: arg1, arg2, arg3

La distribución de los archivos class se suele realizar usando librerías jar y estas se construyen usando el comando jar. El archivo de manifiesto es un descriptor en el que se puede indicar la clase de entrada sin tener que especificarla en el comando java haciendo los archivo jar similar a un ejecutable.

1
2
#!/usr/bin/env bash
jar cvfm holamundojava8.jar src/main/misc/MANIFEST.MF -C target/classes .
1
2
3
Main-Class: io.github.picodotdev.blogbitix.java8.helloworld.Main
Class-Path: libraries/log4j-api-2.11.1.jar libraries/log4j-core-2.11.1.jar

Y la ejecución de del programa contenido en el archivo jar.

1
2
#!/usr/bin/env bash
java -jar holamundojava8.jar "$@"
1
2
$ ./java-jar.sh arg1 arg2 arg3
16:20:33.848 [main] INFO  io.github.picodotdev.blogbitix.java8.helloworld.Main Arguments: arg1, arg2, arg3

Así es la compilación y ejecución de código Java en Java 8 y anteriores, con la introducción de la modularidad a partir de Java 9 esto cambia ya que el classpath queda obsoleto y es reemplazado por el equivalente con módulos module-path.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub.