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?

ut01-abaco.jpg500
Á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

ut01-pascalina.jpg500
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.

ut01-babbage.jpg500
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.

ut01-eniac.jpg500
Matemáticas programadoras de ENIAC. Fuente: Infobae

Tal y como profetizó Ada Lovelace: el hardware es el telar, el programador diseña el patrón

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.

Ejemplo con arquitectura x86

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:

JE→01110100

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:

JNE→01110101

Fuente: x86 Opcodes

Este fallo tan pequeño marca la diferencia entre bloquear o darle acceso a un usuario cuya contraseña es incorrecta.

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:
ut01-compilación.png
Elaboración propia

Del código fuente al código máquina

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.

Actividad en clase: el algoritmo de la burbuja

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:

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).

Podemos resumir todo el proceso de la siguiente manera:
ut01-algoritmo-programa.png

  1. Tienes una idea sobre cómo resolver un problema real siguiendo una secuencia de pasos.
  2. Escribes el código fuente usando una sintaxis de programación en Java.
  3. Utilizas el compilador para traducir el código fuente a código binario.
  4. El código binario es completamente ininteligible por seres humanos, pero perfectamente ejecutable por un procesador.
  5. El procesador ejecuta el código binario y resuelve el problema.
El portero de discoteca

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

  1. Pide el DNI
  2. Mira la fecha de nacimiento y calcula la edad
  3. ¿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:

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:

Los lenguajes de programación se pueden clasificar de muchas maneras:

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.

Java

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:

Teorema de Böhm-Jacopini

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:

  1. Secuencia: las instrucciones se ejecutan una tras otra, en orden descendente estricto.
  2. 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).
  3. Iteración (bucle/repetición): un bloque de instrucciones se repite en bucle mientras se cumpla una condición determinada (MIENTRAS).
El algoritmo de la burbuja, versión estructurada

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.

La magia de la sangría y los bloques

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
ut01-terminal.png Terminal Marca el punto de inicio o fin del algoritmo
ut01-entrada-salida.png Entrada/Salida Captura datos introducidos por el usuario (entrada) o muestra resultados en pantalla (salida)
ut01-proceso.png Proceso Indica la instrucción que debe realizarse. Suelen ser asignaciones, cálculos matemáticos o transformaciones de datos
ut01-decision.png Decisión Evalúa una condición y bifurca el camino en 2 opciones (sí/no)
ut01-flujo.png 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
ut01-secuencia.png ut01-selección.png ut01-iteración.png
Ejemplo

ut01-ejemplo-diagrama.png

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:

Traducción del ejemplo anterior a pseudocódigo

Si tomamos exactamente la misma lógica del diagrama de flujo del ejemplo anterior (donde X=6 e Y=7), su representación en pseudocódigo se escribiría así:

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.

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:2px

Anatomí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

  1. 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.
  2. Importaciones (import): permite reutilizar funcionalidades ya existentes en las librerías de Java (por ejemplo, java.util.Scanner para entrada de datos por teclado).
  3. 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).
  4. 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.
  5. 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!");
    }
    
}
Desglose línea a línea

  • 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.java
    
  • public class HolaMundo: define la clase pública HolaMundo (que debe guardarse como HolaMundo.java).
  • public static void main(String[] args): cabecera obligatoria del método principal de ejecución.
  • System.out.println(...);: sentencia equivalente al MOSTRAR de 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:2px

Compilació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

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

Algunas reglas a tener en cuenta

  • 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 .

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:

  1. Abre el panel de Extensiones:
    ut01-vscode01.png500
  2. 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.
    ut01-vscode02.png500
  3. 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_HOME y PATH, 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.
    ut01-vscode03.png500

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 > .

  1. Primero buscamos "create java project":
    ut01-proyecto01.png500
  2. A continuación, seleccionaremos "No build tools":
    ut01-proyecto02.png500
  3. Deberemos indicar en qué directorio creamos el proyecto de Java:
    ut01-proyecto03.png500
  4. Y también indicaremos un nombre para el proyecto:
    ut01-proyecto04.png500

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)
Compilación en tiempo real

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.