UT01 - Introducción a la Programación
Conexión legislativa
En esta Unidad de Trabajo nos centraremos en PROG-CE-01.a, PROG-CE-01.b y PROG-CE-01.c:
Introducción
¿Cuál es el origen de la informática? Si nos atenemos a la definición de la RAE, diríamos que se trata del "conjunto de conocimientos científicos y técnicas que hacen posible el tratamiento automático de la información por medio de computadoras" (fuente). El término de "computadora" viene del inglés to compute y se refiere a calcular o realizar cómputos. Para tal fin, el ser humano se ha valido de instrumentos cuyo origen se remonta a varios milenios en el pasado. ¿Cuál es el origen de las computadoras?

Ábaco. Fuente: arielphoto (pixabay)
Se estima que el ábaco fue inventado entre el año 2700 y el 300 antes de Cristo y, desde entonces, el ser humano no ha cesado de inventar dispositivos para facilitar los cálculos. Sin embargo, durante siglos, todas estas herramientas ingeniosas compartían una limitación fundamental: eran hardware dedicado para un propósito único. El ábaco, la Pascalina, el telar de Jacquard... Todos ellos magníficos ingenios para su época, pero todos servían a un propósito específico. Si se quería que la máquina realizara una tarea distinta, la única solución era rediseñar y modificar físicamente su estructura

Pascalina. Fuente: Wikipedia
El primer gran salto hacia las computadoras modernas se produjo con Charles Babbage, en el siglo XIX, con el diseño de su máquina analítica. Esta máquina incluiría memoria, unidad de proceso y capacidad de leer instrucciones mediante tarjetas perforadas. Ada Lovelace, figura importantísima en la historia de la informática escribiría varios algoritmos pensados para ser ejecutados por la máquina analítica y profetizó: "La máquina analítica puede tejer patrones algebraicos igual que el telar de Jacquard teje flores y hojas". Sin embargo, la tecnología de la época no permitió construir la máquina analítica en su totalidad.

Charles Babbage y su máquina analítica. Fuente: Independent
La máquina reprogramable
En el siglo XX se produjo la verdadera revolución. Alan Turing y John von Neumann formalizaron el modelo de máquina reprogramable basada en componentes electrónicos. Por primera vez se separaba el soporte físico (hardware) de las instrucciones que debe ejecutar (software).
El primer gran exponente de esta época fue el ENIAC (Electronic Numerical Integrator And Computer). La programación de este computador implicaba que sus operarias (pioneras de la informática históricamente invisibilizadas) reconectaran físicamente cientos de cables, que servían como conexiones lógicas para configurar cada nuevo cálculo. No había un programa almacenado en memoria: el código era, literalmente, una maraña de cables.

Matemáticas programadoras de ENIAC. Fuente: Infobae
Pronto se evidenció la necesidad de almacenar instrucciones directamente en la memoria del computador, y de esta idea surgió el código máquina, que es el único lenguaje que entiende un procesador de manera nativa.
El código máquina
Se compone de secuencias de 1 y 0 (lenguaje binario), que representan los estados eléctricos de alto y bajo voltaje y se corresponden con instrucciones directas para el procesador. Programar en lenguaje máquina es una tarea difícil, lenta y propensa a fallos: cometer un error en 1 único bit podía arruinar por completo el programa. Además, es completamente dependiente de la arquitectura física del propio procesador, por lo que procesadores de diferentes fabricantes no comparten instrucciones en código máquina, o incluso diferentes modelos del mismo fabricante.
Existe instrucción en código máquina para saltar condicionalmente se denomina JE (jump if equal). Es la instrucción número 74 y su equivalente en binario es:
Esta instrucción lo que hace es saltar a otra parte del programa cuando se da una condición de igualdad entre 2 valores.
Sin embargo, un mínimo fallo en el último bit, cambiando el último 0 por un 1, pasamos a la instrucción número 75: JNE (jump if not equal). Es decir: salta si no es igual, justo lo contrario a lo que queremos. Su equivalente en binario es:
Fuente: x86 Opcodes
Este fallo tan pequeño marca la diferencia entre bloquear o darle acceso a un usuario cuya contraseña es incorrecta.
- Con la instrucción correcta (
01110100/JE): el programa comprueba si las contraseñas de acceso coinciden y, si son iguales, da paso al usuario. - Con 1 bit alterado (
01110101/JNE): el programa salta a la rutina de acceso sólo cuando las claves NO son iguales, abriendo la puerta al atacante y bloqueando al usuario legítimo.
Un simple cambio de voltaje en un transistor altera por completo la seguridad y la lógica de la aplicación. Esta fragilidad, sumada a la imposibilidad de reutilizar el código entre distintos procesadores, impulsó la creación de mecanismos de traducción y lenguajes de alto nivel que aíslan al desarrollador de las complejidades del hardware.
Abstracción y código fuente
La abstracción es un principio de diseño que consiste en simplificar la complejidad de un sistema al ocultar los detalles internos innecesarios y exponer sólo las características y comportamientos esenciales para el desarrollador. Este concepto permite tratar componentes complejos como "cajas negras", facilitando la creación de software modular, reutilizable y más fácil de mantener.
¿Cómo se relaciona este concepto con el código máquina? Un desarrollador no debería de tener que recordar que 01110100 es una instrucción de salto cuando 2 valores son iguales en la arquitectura x86, sino que debería poder expresar esa misma idea mediante estructuras legibles. Ahí entra en juego el código fuente.
El código fuente es un conjunto de instrucciones escritas en un lenguaje de programación. Estos lenguajes utilizan una sintaxis más cercana al lenguaje humano (siempre que consideremos que los ingleses son humanos).
La abstracción nos permite independizarnos de la arquitectura física del procesador, pero el único lenguaje que entiende el procesador es el binario, por lo que todo código fuente tiene que pasar por un proceso de traducción llamado compilación. El proceso sería el siguiente:

Elaboración propia
Necesitarás un entorno basado en Linux, el compilador gcc y la herramienta objdump. gcc viene instalado por defecto. Para objdump necesitarás instalar binutils. En sistemas basados en Debian (como Ubuntu), ejecuta:
sudo apt install binutils
Crea un fichero llamado hola_mundo.c, copia y pega el siguiente contenido:
#include <stdio.h>
int main() {
printf("¡Hola, mundo!\n");
return 0;
}
Guarda el fichero, abre la terminal, sitúate en el mismo directorio que el fichero recién creado y ejecuta el siguiente comando:
gcc hola_mundo.c -o hola_mundo
Esto compilará el código fuente y generará un ejecutable llamado hola_mundo. Para ejecutarlo, en el mismo terminal, escribe el siguiente comando:
./hola_mundo
A continuación vamos a inspeccionar el código máquina, lo que necesita el procesador para ejecutar nuestro programa. Ejecuta el siguiente comando:
objdump -d hola_mundo
Esto mostrará una representación de texto de las instrucciones binarias que leerá el procesador. Vamos a buscar, en la salida que devuelve este comando, el apartado <main> para localizar las instrucciones que hemos escrito en hola_mundo.c. El resto de código no nos interesa tanto, pero es necesario que esté para que nuestro programa funcione. En la sección <main> veremos algo así:
0000000000400466 <main>:
400466: 55 push %rbp
400467: 48 89 e5 mov %rsp,%rbp
40046a: bf 78 11 40 00 mov $0x401178,%edi
40046f: e8 fc fe ff ff call 400370 <puts@plt>
400474: b8 00 00 00 00 mov $0x0,%eax
400479: 5d pop %rbp
40047a: c3 ret
Los caracteres representados por pares en la segunda columna (55, 48, 89, e5...) son una representación hexadecimal del código binario que leerá el procesador y se corresponderían a algo como:
01010101 01001000 10001001 11100101...
Por suerte, nuestro viaje por el temeroso mundo del código máquina termina aquí. Nosotros aprenderemos a programar en lenguajes de alto nivel. Más concretamente en Java.
Algoritmo
Antes de empezar a programar debemos hacer otra parada en un concepto fundamental, que es el de algoritmo. Al igual que con los computadores nos remontamos a varios cientos de años antes de Cristo, para hablar de algoritmos también hay que viajar en el tiempo. Más concretamente, al siglo 8 (después de Cristo, nos pilla más cerquita). En ese siglo nace Muhammad ibn Musa al-Khwarizmi, matemático que introdujo los números arábigos en Europa y dio nombre a lo que vamos a estudiar en este apartado.
Más allá de esta curiosidad, lo que verdaderamente nos interesa es saber que un algoritmo no es más que un conjunto finito de instrucciones bien definidas que nos ayudan a resolver un problema.
Un ejemplo genial de cómo desglosar una tarea cotidiana con la precisión estricta que exigiría un ordenador lo encontramos en la literatura:
Nosotros, como seres humanos, admitimos y entendemos ambigüedades, nuestro comportamiento está definido por nuestra educación, nuestro contexto cultural y por conocimiento teórico y práctico acumulado, pero un ordenador no es así. Un ordenador es una máquina puramente lógica, rígida y literal: carece de sentido común, de contexto implícito y de la capacidad de deducir nuestras intenciones. Ante un enunciado en el que no se especifican todas las condiciones, los humanos tendemos a asumir el contexto faltante, derivando en interpretaciones ambiguas:
Es muy importante diferenciar entre algoritmo y programa. Un algoritmo es la solución teórica y conceptual a un problema: el conjunto de pasos lógicos, ordenados e independientes del lenguaje que indican qué hay que hacer. Un programa, es la implementación práctica de ese algoritmo escrita en la sintaxis concreta de un lenguaje de programación para que una máquina específica pueda ejecutarlo.
1. Sitúate en la posición más a la izquierda
2. Compara el alumno de esa posición con el de tu derecha. ¿El número es más alto que el de la derecha?
- Sí: intercambia sus posiciones y levanta la mano
3. Avanza 1 posición a la derecha
4. ¿Has llegado a la posición más a la derecha?
- No: vuelve al paso 2
- Sí: ¿tienes la mano levantada?
- Sí: baja la mano y vuelve al paso 1
5. Fin del algoritmo
Algoritmo VS Programa
Un algoritmo es una idea abstracta, independiente del idioma, una forma de resolver problemas describiéndolos como una secuencia finita de pasos a seguir. Los ordenadores no entienden de ideas abstractas, sólo entienden de instrucciones.
Por otro lado, un programa es la implementación concreta de un algoritmo usando la sintaxis de un lenguaje de programación. Aquí es donde cobra importancia el código fuente que mencionamos anteriormente:
Podemos resumir todo el proceso de la siguiente manera:

- Tienes una idea sobre cómo resolver un problema real siguiendo una secuencia de pasos.
- Escribes el código fuente usando una sintaxis de programación en Java.
- Utilizas el compilador para traducir el código fuente a código binario.
- El código binario es completamente ininteligible por seres humanos, pero perfectamente ejecutable por un procesador.
- El procesador ejecuta el código binario y resuelve el problema.
Imagínate que eres el dueño de una discoteca. Te das cuenta de que resolver el problema de que se te cuelen menores de edad es muy sencillo: sólo tienes que poner un portero y darle unas instrucciones muy claras
- Pide el DNI
- Mira la fecha de nacimiento y calcula la edad
- ¿Es mayor de 18 años?
- SÍ: puedes pasar
- NO: no puedes pasar
No importa si el portero habla inglés, español, alemán o chino. Los pasos son siempre los mismos.
¿Y si el portero fuera un robot? En tal caso deberemos de utilizar un lenguaje de programación. Aquí tienes varios ejemplos:
if (edad >= 18) {
System.out.println("Puedes entrar a la discoteca.");
} else {
System.out.println("Acceso denegado: eres menor de edad.");
}
if (edad >= 18) {
printf("Puedes entrar a la discoteca.");
} else {
printf("Acceso denegado: eres menor de edad.");
}
if edad >= 18:
print("Puedes entrar a la discoteca.")
else:
print("Acceso denegado: eres menor de edad.")
if (edad >= 18) {
console.log("Puedes entrar a la discoteca.")
} else {
console.log("Acceso denegado: eres menor de edad.")
}
Como ves, todos se parecen y son fáciles de entender "a ojo".
Lenguaje de Programación
Los seres humanos nos comunicamos usando el lenguaje natural. Las máquinas, o incluso los componentes de hardware dentro de una máquina, lo hacen mediante el lenguaje máquina.
Los lenguajes de programación comparten un poco de lenguaje humano (legibilidad) y un poco de lenguaje máquina (estructuración y lógica rígida). Son el punto de encuentro entre la flexibilidad del pensamiento humano y la precisión matemática que exige un procesador.
Al igual que en el lenguaje natural componemos frases con sujeto + verbo + predicado y siguiendo una serie de reglas sintácticas y gramaticales, en un lenguaje de programación pasa exactamente lo mismo:
- Existe un número finito de palabras reservadas que podemos utilizar, el léxico.
- Las frases, o instrucciones, deben construirse siguiendo una sintaxis estricta.
Los lenguajes de programación se pueden clasificar de muchas maneras:
- Según la cercanía a la máquina: bajo nivel, nivel medio o alto nivel. Ya hablamos de esto aquí.
- Según el propósito del lenguaje: propósito general o específico.
- Según la manera en que se ejecutan: compilados o interpretados.
- Según cómo afrontan las tareas a realizar: imperativos o declarativos.
- Según el estilo de programación: procedimentales u orientados a objetos.
- ...
Estas categorías de clasificación no son más que la superficie de un ecosistema en constante evolución, ni a día de hoy los lenguajes son tan categóricos.
Es un lenguaje de propósito general, de alto nivel y orientado a objetos, imperativo y con un modelo híbrido de ejecución (compila a un formato intermedio que se ejecuta en una máquina virtual, la JVM - Java Virtual Machine)
Programa
Independientemente del lenguaje de programación, cualquier programa informático no es más que una secuencia de instrucciones organizada para resolver un problema. A mediados de los años 60, la informática sufría la crisis del software: los programas no tenían una estructura clara y abusaban de las instrucciones de salto incondicional (llamadas GOTO). Esto producía lo que se conocía como código espagueti, difícil de leer y mantener.
El teorema de Böhm-Jacopini y la programación estructurada
La solución a este caos llegó en 1966 de la mano de Corrado Böhm y Giuseppe Jacopini, quienes demostraron un principio clave que dio origen a la programación estructurada:
Cualquier algoritmo computable puede expresarse combinando únicamente tres estructuras de control básicas: Secuencia, Selección e Iteración.
Este teorema demostró que no se necesitan saltos caóticos ni instrucciones complejas para resolver un problema informático. Todo programa, por masivo o complejo que sea, se construye ensamblando tres bloques fundamentales:
- Secuencia: las instrucciones se ejecutan una tras otra, en orden descendente estricto.
- Selección (condicional): el programa evalúa una condición lógica y toma un camino u otro en función de si es verdadera o falsa (
SI... ENTONCES... SINO). - Iteración (bucle/repetición): un bloque de instrucciones se repite en bucle mientras se cumpla una condición determinada (
MIENTRAS).
Si recuerdas, el algoritmo de la burbuja que se describió anteriormente tiene las líneas numeradas y varias instrucciones de "ir al paso X". Estas son las famosas instrucciones GOTO. Podemos identificar perfectamente los 3 bloques del teorema:
- Secuencia: instrucciones directas. Colocarse a la izquierda, intercambiar posiciones, avanzar 1 posición, levantar la mano, bajar la mano
- Selección: toma de decisiones. ¿El número es más alto que el de la derecha?, ¿está la mano levantada?
- Iteración: repetición de bloques de instrucciones. Repetir mientras no se llegue al final, repetir mientras se tenga la mano levantada
Podríamos reescribir el algoritmo de la burbuja siguiendo el teorema de Böhm-Jacopini de la siguiente manera
REPETIR
Baja la mano
Sitúate en la posición más a la izquierda
REPETIR
SI número actual > número de la derecha ENTONCES
Intercambia posiciones
Levanta la mano
Avanza 1 posición
MIENTRAS tu posición no sea la última
MIENTRAS mano levantada
Al eliminar las instrucciones GOTO y sustituirlas por iteraciones anidadas y selecciones, hemos transformado una secuencia caótica en un bloque legible, modular y mantenible. Ese es exactamente el propósito de la programación estructurada: construir algoritmos donde el flujo de ejecución sea siempre claro y predecible.
Fíjate en cómo la sangría (indentación) deja claro qué bloques de instrucciones están "dentro de otros". Esta versión estructurada no solo es más fácil de entender para un humano, sino que se traduce casi directamente de forma 1:1 a un lenguaje de programación real como Java usando bucles do-while y sentencias if.
Representación gráfica: diagramas de flujo
Antes de empezar a escribir código (ya queda poquito, te lo prometo) es necesario aprender a diseñar adecuadamente soluciones algorítmicas siguiendo las reglas del teorema de Böhm-Jacopini. Para facilitar esto, nos servimos de los diagramas de flujo, también llamados organigramas.
Un diagrama de flujo es la representación gráfica de un algoritmo. Utiliza una serie de figuras geométricas estandarizadas conectadas entre sí por líneas de flujo (flechas) que indican el orden estricto en que una computadora debe ejecutar cada instrucción.
| Símbolo | Nombre | Función |
|---|---|---|
![]() | Terminal | Marca el punto de inicio o fin del algoritmo |
![]() | Entrada/Salida | Captura datos introducidos por el usuario (entrada) o muestra resultados en pantalla (salida) |
![]() | Proceso | Indica la instrucción que debe realizarse. Suelen ser asignaciones, cálculos matemáticos o transformaciones de datos |
![]() | Decisión | Evalúa una condición y bifurca el camino en 2 opciones (sí/no) |
![]() | Línea de flujo | Muestra la dirección y el sentido en el que avanza la ejecución del programa |
Hay más símbolos, pero estos son los más importantes y los que nos permiten diseñar cualquier programa.
Las 3 estructuras de Böhm-Jacopini
Cada una de las 3 estructuras básicas tiene un patrón visual inconfundible dentro de un diagrama de flujo:
| Secuencia | Selección | Iteración |
|---|---|---|
![]() | ![]() | ![]() |

Compara 2 números y realiza una suma o una resta en función de la desigualdad de sus valores. Primero guarda el valor 6 en X y el valor 7 en Y. Luego compara X con Y. Si X es mayor que Y, guarda la suma en Z. Si X no es mayor que Y, guarda la resta en Z. Por último, muestra el valor de Z.
1, porque como 6 no es mayor que 7, se guarda en Z el resultado de 7 - 6
Representación textual: pseudocódigo
El pseudocódigo es la representación en lenguaje natural estructurado de un algoritmo. A diferencia del diagrama de flujo (que es gráfico), el pseudocódigo utiliza palabras clave, sangría (llamada indentación) y construcciones similares a las de un lenguaje de programación de alto nivel, pero sin atarse a las reglas rígidas de sintaxis de ningún lenguaje concreto.
Sirve como el puente perfecto entre el diagrama de flujo y el código fuente definitivo, permitiéndonos centrarnos exclusivamente en la lógica del algoritmo.
Palabras clave habituales
Aunque no existe un estándar único e inflexible para escribir pseudocódigo, la mayoría de convenciones utilizan una serie de términos estructurados:
- Estructura general:
ALGORITMO,INICIO,FIN - Entrada/Salida:
LEER,MOSTRAR/ESCRIBIR - Asignación:
=o<-(para guardar un valor en una variable) - Selección:
SI ... ENTONCES ... SINO ... FIN_SI - Iteración:
MIENTRAS ... HACER ... FIN_MIENTRAS,REPETIR ... MIENTRAS / HASTA QUE
Traducción del ejemplo anterior a pseudocódigo
Si tomamos exactamente la misma lógica del diagrama de flujo del ejemplo anterior (donde
ALGORITMO CompararNumeros
INICIO
X = 6
Y = 7
SI X > Y ENTONCES
Z = X + Y
SINO
Z = Y - X
FIN_SI
MOSTRAR Z
FIN
Java
Java es un lenguaje de programación de alto nivel, orientado a objetos y de propósito general. Como vimos anteriormente, una de sus características principales es su modelo de ejecución híbrido: el código fuente no se traduce directamente a código máquina, sino a un formato intermedio denominado bytecode (.class), el cual es ejecutado por la Máquina Virtual de Java (JVM). Esto permite que el mismo programa funcione en cualquier sistema operativo sin necesidad de recompilarlo ("Write Once, Run Anywhere"). De esta forma, combina la portabilidad y flexibilidad de los lenguajes interpretados con el alto rendimiento y la detección temprana de errores de los lenguajes compilados.
Para entender bien cómo funciona el desarrollo y la ejecución de aplicaciones de software en Java, es necesario mencionar los términos JDK, JRE y JVM.
- JDK (Java Development Kit): kit completo para el desarrollador. Además del JRE, contiene herramientas como el compilador
javac, el depuradorjdby generadores de documentación. El compilador transforma el código fuente en bytecode, una especie de código máquina intermedio, portable e independiente del hardware. Genera ficheros.class. - JRE (Java Runtime Environment): es el paquete de ejecución para usuarios. Incluye la JVM y librerías de código estándar de Java. Es el mínimo necesario para poder ejecutar una aplicación de Java en cualquier sistema.
- JVM (Java Virtual Machine): máquina virtual de Java. Es el entorno encargado de ejecutar el bytecode de los ficheros
.class. Para ello, las traduce mediante un compilador JIT (Just-In-Time) al código máquina del sistema y arquitectura en el que se encuentre ejecutando. Existen distintas versiones de la JVM adaptadas a diferentes Sistemas Operativos y arquitecturas de hardware.
graph TD
subgraph JDK ["JDK (Java Development Kit)"]
JDK_TOOLS["Herramientas de desarrollo:
javac, jdb, javadoc..."]
subgraph JRE ["JRE (Java Runtime Environment)"]
JRE_LIBS["Librerías estándar de la plataforma:
java.lang, java.util..."]
subgraph JVM ["JVM (Java Virtual Machine)"]
JVM_ENGINE["Motor de ejecución:
intérprete + compilador JIT"]
end
end
end
style JDK fill:#f9f9f9,stroke:#333,stroke-width:2px
style JRE fill:#e1f5fe,stroke:#0288d1,stroke-width:2px
style JVM fill:#e8f5e9,stroke:#388e3c,stroke-width:2pxAnatomía de un archivo .java
Para que el compilador de Java (javac) entienda nuestro código, debemos organizar las instrucciones siguiendo una estructura estricta de bloques funcionales anidados.
Bloques principales
- Paquete (
package): declara el espacio de nombres o carpeta donde se organiza el archivo. Evita conflictos de nombres y permite estructurar proyectos de gran tamaño. - Importaciones (
import): permite reutilizar funcionalidades ya existentes en las librerías de Java (por ejemplo,java.util.Scannerpara entrada de datos por teclado). - Clase (
public class): es el contenedor principal del código. En Java, todo el código debe residir dentro de una clase. El nombre de la clase pública debe coincidir exactamente con el nombre del fichero (NombreClase.java). - Método principal (
public static void main): es el punto de entrada de la aplicación. La JVM busca este método exacto para iniciar la ejecución secuencial del programa. - Sentencias: instrucciones individuales que realizan acciones (asignaciones, llamadas a métodos, operaciones matemáticas). En Java, toda sentencia finaliza obligatoriamente con un punto y coma
;.
¡Hola, Mundo!
A continuación se muestra la implementación mínima de un programa ejecutable en Java:
package es.daw.ut01;
public class HolaMundo {
public static void main(String[] args) {
System.out.println("¡Hola, mundo!");
}
}
package es.daw.ut01;: declara la pertenencia de la clase al paquete indicado. El fichero que contiene el código fuente deberá estar ubicado en una estructura de directorios tal que así:es └── daw └── ut01 └── HolaMundo.javapublic class HolaMundo: define la clase públicaHolaMundo(que debe guardarse comoHolaMundo.java).public static void main(String[] args): cabecera obligatoria del método principal de ejecución.System.out.println(...);: sentencia equivalente alMOSTRARde pseudocódigo. Imprime por pantalla el texto indicado y añade un salto de línea.{y}: Delimitan el inicio y el fin de un bloque de código o ámbito (scope).
Compilación y ejecución
El flujo completo de trabajo con un programa de Java empieza con la redacción del código fuente y termina con la generación de un fichero ejecutable por la JVM:
flowchart TD
A["Código Fuente
HolaMundo.java"] -->|"1. Compilador (javac)"| B["Bytecode
es/daw/ut01/HolaMundo.class"]
B -->|"2. Empaquetador (jar .)"| C["Archivo Ejecutable
HolaMundo.jar"]
C -->|"3. Máquina Virtual (java -jar)"| D["Código Máquina
(Windows, Linux, macOS)"]
style A fill:#fff3e0,stroke:#e65100,stroke-width:2px
style B fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
style C fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:2pxCompilación y empaquetado desde la terminal
Para gestionar correctamente las dependencias de los paquetes y la generación del fichero .jar, se siguen los siguientes pasos en la consola:
Compilación con javac
El compilador javac transforma nuestro código (contenido en el fichero .java) a un fichero de bytecodes con extensión .class. Si intentamos compilar directamente un archivo que incluye una declaración de paquete (por ejemplo, package es.daw.ut01;) usando el comando básico javac HolaMundo.java, el archivo binario .class se generará en el mismo directorio que el código fuente, lo cual no es deseable ya que, en el momento de empaquetar la aplicación, incluiríamos también los ficheros .java. Por ello, es habitual crear un directorio bin en el que se guarden los ficheros compilados .class para su posterior empaquetado:
javac -d bin es/daw/ut01/HolaMundo.java
-d bin: indica ajavacel directorio de destino (destination). El compilador creará automáticamente la estructura de subdirectorios correspondiente al paquete (bin/es/daw/ut01/) y colocará ahí dentro el archivoHolaMundo.class.
Creación del archivo de manifiesto (MANIFEST.MF)
Es necesario indicar de forma explícita cuál es la clase principal (el punto de entrada de ejecución). Es la que contiene el método public static void main. Cuando un programa es muy grande, no es razonable que la JVM inspeccione todos los ficheros buscando ese método, por lo que se define un fichero de texto simple llamado "archivo de manifiesto". Creamos un archivo MANIFEST.MF que contenga lo siguiente:
Main-Class: es.daw.ut01.HolaMundo
- Es necesario indicar el nombre completo, incluyendo el paquete al que pertenece (
es.daw.ut01.HolaMundo). - Es obligatorio dejar una línea en blanco al final.
Empaquetado con la herramienta jar
Una vez compilado el código y preparado el manifiesto, empaquetamos la aplicación en un fichero comprimido .jar mediante la utilidad jar
jar cmf MANIFEST.MF HolaMundo.jar -C bin .
c(create): indica que se va a crear un nuevo fichero JAR.m(manifest): especifica que se va a incluir un archivo de manifiesto.f(file): indica el nombre que tendrá el archivo de salida (HolaMundo.jar).-C bin .(Change directory): empaqueta todo el contenido (.) del directoriobin. Esto evita que la estructura de ficheros empiece en la raíz del paquetees/y no incluya el código fuente.java.
Ejecución en la JVM
Una vez generado el ejecutable, cualquier equipo que disponga del JRE podrá ejecutar la aplicación, independientemente de la plataforma:
java -jar HolaMundo.jar
Dando el salto a un IDE
Aunque compilar y empaquetar manualmente la aplicación nos permite apreciar el ciclo completo y el modo en que se gestionan los paquetes y las estructuras de directorios en proyectos reales de Java, en proyectos reales, este proceso manual es muy tedioso e ineficiente. Por esa razón, en el desarrollo de software profesional se utilizan Entornos de Desarrollo Integrados (IDEs), o editores de texto avanzados, como Visual Studio Code (VSCode).
Estas herramientas permiten crear tareas que reducen el proceso de compilado, empaquetado y ejecución a 1 click, dando al desarrollador una experiencia mucho más fluida y aumentando considerablemente su productividad.
Configuración del entorno en VSCode
VSCode no es un IDE como tal. Es un editor de texto extensible. Precisamente por ser extensible, podemos instalar una serie de plugins que lo convierten en un IDE completo.
Para añadir soporte completo para el desarrollo de Java, aconsejo instalar el conjunto de extensiones oficial:
- Abre el panel de Extensiones:

- Usando el término de búsqueda "java" encontrarás un pack de extensiones muy popularizado en la industria de desarrollo en Java llamado "Extension Pack for Java". Este pack incluye seis extensiones clave creadas por Microsoft y Red Hat que transforman el editor en un entorno de desarrollo completo.

- Haz click en el botón "Install" para instalar la extensión. Si has instalado correctamente el JDK y las variables de entorno de
JAVA_HOMEyPATH, VSCode detectará automáticamente la instalación de Java presente en tu sistema y dejará el entorno listo para empezar a trabajar sin necesidad de realizar configuraciones adicionales.

Creación de un proyecto
El pack de extensiones instalado nos permite crear diferentes tipos de proyectos de Java pero, de momento, sólo nos interesa uno: el proyecto sin herramientas de construcción avanzadas (No Build Tools). Esta manera es la ideal para proyectos sencillos.
Para crear un proyecto de Java, podemos hacer uso del atajo de teclado para la ejecución de comandos en VSCode: Ctrl + Shift + P. Si esto no funciona, utiliza la barra superior de búsqueda e introduce el carácter > .
- Primero buscamos "create java project":

- A continuación, seleccionaremos "No build tools":

- Deberemos indicar en qué directorio creamos el proyecto de Java:

- Y también indicaremos un nombre para el proyecto:

Con estos sencillos pasos habremos creado un proyecto de Java en VSCode.
Estructura del proyecto
Una vez creado el proyecto, VSCode genera una arquitectura de carpetas limpia que replica la separación entre código fuente y binarios que aprendimos a gestionar desde la terminal:
HolaMundo/
├── .vscode/ <-- Ajustes de configuración del entorno para el proyecto
│ └── settings.json
├── bin/ <-- Salida automática de los archivos .class compilados
├── lib/ <-- Ubicación para librerías y dependencias externas (.jar)
└── src/ <-- Código fuente de la aplicación (.java)
Cada vez que guardas un fichero de código (Ctrl + S), VSCode ejecuta automáticamente en segundo plano el equivalente al comando javac -d bin, manteniendo la carpeta bin/ siempre actualizada con los archivos .class resultantes.
Como habrás apreciado, este proyecto no incluye una organización por paquetes. Al ser un proyecto sencillo con 1 único fichero no es necesaria esa organización, pero en proyectos más grandes sí que será necesario.







