Saltar a contenido

Práctica 1.3: Código intermedio y máquina virtual Java

P14 - Código intermedio y máquina virtual Java

En esta práctica escribirás una suma sencilla en Java, generarás el archivo .class con javac, lo ejecutarás mediante la JVM e inspeccionarás sus instrucciones con javap. Así podrás reconocer qué cambia entre el código fuente y el bytecode, y por qué la portabilidad requiere un entorno de ejecución compatible. La actividad desarrolla el apartado 1.3. Código intermedio y se centra en el CE 1.d. Tiempo estimado: 60-75 minutos; la comprobación en otro sistema es una ampliación de unos 15 minutos.

1. Objetivos

  • Generar bytecode Java a partir de un archivo fuente y reconocer el papel de javac, .class y la JVM.
  • Ejecutar el mismo programa mediante la JVM e inspeccionar algunas instrucciones de bytecode con javap.
  • Explicar qué aporta la portabilidad y qué requisitos de versión, entorno de ejecución y dependencias siguen siendo necesarios.
  • Identificar prácticas de trabajo reutilizables en otros procesos de compilación y construcción de software.

2. Relación con RA y criterios de evaluación

Código Descripción
RA 1 Reconoce los elementos y herramientas que intervienen en el desarrollo de un programa informático, analizando sus características y las fases en las que actúan hasta llegar a su puesta en funcionamiento.
CE 1.d Se han reconocido las características de la generación de código intermedio para su ejecución en máquinas virtuales.

El criterio principal es CE 1.d: la actividad hace visible la generación de bytecode y su ejecución en una máquina virtual, en vez de limitarse a describir esos conceptos.

3. Requisitos y recursos

  • Un ordenador con terminal o consola y un JDK instalado. Se recomienda JDK 17 o posterior, con java, javac y javap disponibles en el PATH.
  • Un editor de texto o un IDE. El IDE es opcional; utilizar la terminal permite ver las órdenes y los archivos generados en cada paso.
  • La teoría 1.3. Código intermedio. Como repaso, puedes consultar 1.2. Código fuente, objeto y ejecutable.
  • Trabajo individual o en grupos de 2-3 personas. Si trabajáis en grupo, repartid los papeles de edición, ejecución de comandos y registro de evidencias, y cambiadlos durante la actividad.

Un compilador en línea puede servir para escribir y ejecutar el ejemplo, pero algunos servicios ocultan el .class o no incluyen javap. Para completar la inspección del bytecode, utiliza un JDK local o un entorno que permita acceder a esos archivos y herramientas.

4. Pasos a seguir

4.1. Preparar el entorno y escribir el código fuente

  1. Crea una carpeta para la práctica y abre una terminal en ella. Comprueba qué herramientas tienes instaladas y registra sus versiones:

    java --version
    javac --version
    javap -version
    

    Si alguna orden no se reconoce, verifica que has instalado un JDK y que su carpeta de herramientas está en el PATH. Anota también el sistema operativo y su versión.

  2. Crea un archivo llamado exactamente Suma.java con este programa. El nombre del archivo coincide con el de la clase pública:

    public class Suma {
        public static void main(String[] args) {
            int num1 = 5;
            int num2 = 7;
            int resultado = num1 + num2;
            System.out.println("El resultado es: " + resultado);
        }
    }
    

    Antes de continuar, predice qué mostrará el programa. Suma.java es el código fuente: un archivo de texto que puedes leer y modificar, pero que la CPU no ejecuta directamente como instrucciones nativas.

4.2. Compilar para generar bytecode

  1. Desde la carpeta que contiene Suma.java, ejecuta:

    javac Suma.java
    

    Si la compilación termina sin errores, normalmente no verás ningún mensaje. Comprueba que aparece Suma.class. Si hay un diagnóstico, léelo completo y corrige el problema antes de seguir.

  2. Distingue los dos archivos: Suma.java es el código fuente; Suma.class contiene bytecode para la JVM. El archivo .class no es un ejecutable nativo que puedas iniciar directamente desde el sistema operativo.

4.3. Ejecutar el bytecode en la JVM

  1. En la misma carpeta, solicita a la herramienta java que cargue la clase y ejecute su método main:

    java Suma
    

    Comprueba que la salida es:

    El resultado es: 12
    
  2. Explica el recorrido observado: javac transforma el fuente en bytecode y la JVM carga y ejecuta ese bytecode. Una implementación de JVM puede interpretarlo y/o compilar partes a código nativo con JIT; no des por hecho que todas las JVM utilizan internamente una única estrategia.

4.4. Inspeccionar las instrucciones de bytecode

  1. Ejecuta javap para mostrar una representación legible de las instrucciones:

    javap -c Suma
    

    Localiza en la salida el método main y alguna instrucción relacionada con los valores numéricos, la suma —por ejemplo iadd— y la llamada que muestra el resultado. La salida puede variar según la versión de Java y el compilador; javap muestra bytecode, no el código fuente ni las instrucciones nativas concretas de tu CPU.

  2. Anota qué información puedes deducir de la salida y cuál no: las instrucciones permiten reconocer operaciones del programa, pero no describen por sí solas la plataforma física donde se ejecutará el bytecode.

4.5. Modificar el fuente y repetir el proceso

  1. Cambia los valores de num1 y num2 a 17 y 25. Antes de ejecutar nada, predice el resultado y explica por qué el archivo .class anterior todavía representa la versión previa del programa.

  2. Guarda los cambios, vuelve a compilar y ejecuta otra vez:

    javac Suma.java
    java Suma
    

    Comprueba que aparece El resultado es: 42. Si no coincide, revisa el archivo guardado, los valores, la carpeta desde la que ejecutas las órdenes y si has repetido la compilación.

4.6. Portabilidad y prácticas de trabajo reutilizables

  1. Explica si bastaría con copiar Suma.class a otro equipo para ejecutarlo. Incluye las condiciones que deben cumplirse: debe haber una JVM compatible con la versión del bytecode y estar disponibles las bibliotecas y demás dependencias necesarias. Como ampliación, copia el .class a otro sistema operativo con una JVM compatible e intenta ejecutar java Suma sin llevar el archivo fuente ni recompilarlo. Registra el resultado o el motivo por el que no has podido hacer la prueba.

  2. Identifica al menos cuatro prácticas generales de trabajo que te sirvan también en otras herramientas o lenguajes. Puedes partir de estas ideas y justificarlas con lo que has hecho:

    • comprobar las versiones de las herramientas y el entorno de destino;
    • separar el código fuente de los artefactos generados y conservar la fuente como referencia;
    • registrar las órdenes y comprobar el resultado de cada etapa antes de avanzar;
    • leer los diagnósticos completos y corregir el origen del problema, no solo ocultar el mensaje;
    • repetir la construcción después de cambiar el fuente y evitar confundir un artefacto antiguo con uno recién generado;
    • verificar los requisitos de ejecución y no confundir portabilidad con ausencia de dependencias.

5. Comprobaciones de finalización

  • Has registrado las versiones de Java y del compilador y sabes qué sistema operativo utilizas.
  • El archivo Suma.java compila sin errores y se genera Suma.class.
  • java Suma muestra El resultado es: 12; tras cambiar los operandos y volver a compilar, muestra El resultado es: 42.
  • Has ejecutado javap -c Suma y puedes señalar instrucciones de bytecode sin presentarlas como código fuente o código máquina nativo.
  • Puedes explicar la relación entre fuente, bytecode, JVM y plataforma, así como las condiciones que limitan la portabilidad.
  • Has identificado y justificado al menos cuatro prácticas generales de trabajo y conservas las evidencias solicitadas.

6. Ejercicios y entregables

  1. 1.a. Completa una tabla con Suma.java, Suma.class, javac, java, javap y la JVM.
    • 1.a.1. ¿Qué representa o hace cada elemento?
    • 1.a.2. ¿En qué momento interviene cada elemento?
    • 1.a.3. ¿Cuál es código fuente, cuál es bytecode, cuáles son herramientas y cuál es el entorno de ejecución?
  2. Responde con tus palabras:
    • 2.a. ¿Qué diferencia hay entre el código fuente y el bytecode de Suma?
    • 2.b. ¿Qué hace javac y qué hace la herramienta java?
    • 2.c. ¿Por qué no se ejecuta Suma.class directamente haciendo doble clic o escribiendo su nombre como si fuera un programa nativo?
    • 2.d. ¿Qué representa iadd en la salida de javap -c?
      • 2.d.1. ¿Por qué esa salida no es código máquina de tu procesador?
    • 2.e. ¿Qué aporta la JVM a la portabilidad?
      • 2.e.1. ¿Qué debe seguir siendo compatible en el equipo de destino?
    • 2.f. Compara este proceso con la compilación nativa habitual de C o C++.
      • 2.f.1. ¿Qué artefacto se distribuye en cada caso?
      • 2.f.2. ¿Qué tendrías que comprobar para ejecutar el programa en otro sistema?
  3. 3.a. Cambia los operandos a 17 y 25.
    • 3.a.1. Antes de ejecutar, incluye tu predicción del resultado.
    • 3.a.2. ¿Qué salida obtienes al ejecutar?
    • 3.a.3. Explica qué ocurriría si editases Suma.java pero ejecutases el .class antiguo sin recompilar.
    • 3.b. Guarda el fuente, vuelve a compilarlo con javac y ejecuta la clase actualizada con java Suma. Registra las órdenes. En este flujo no se usa un comando de enlazado separado como en la práctica de GCC; la JVM carga, verifica y resuelve las clases durante la ejecución.
  4. 4.a. Enumera al menos cuatro prácticas generales de trabajo observadas en la actividad y explica cómo ayudan a evitar errores o a reproducir el proceso.
  5. 5.a. Entrega Suma.java y un README.md identificado con tu nombre, módulo, unidad y práctica. Incluye las versiones del entorno, las órdenes utilizadas, la tabla, las respuestas, una captura o transcripción de las dos ejecuciones, un fragmento de javap -c y la reflexión sobre las prácticas generales. Adjunta también Suma.class o, si el aula no admite archivos binarios, una evidencia verificable de su generación.
  6. 6.a. (Ampliación opcional) Intenta ejecutar el mismo Suma.class en otro sistema operativo o equipo. Indica qué archivos copiaste, la versión de JVM disponible y si hubo algún requisito de versión o dependencia que afectase al resultado. La prueba en otro sistema es opcional.

7. Solución orientativa

El programa inicial genera Suma.class al ejecutarse javac Suma.java; java Suma carga la clase mediante una JVM y muestra El resultado es: 12. javap -c Suma presenta instrucciones de bytecode; entre ellas se puede reconocer la suma entera mediante iadd, aunque la secuencia exacta y sus índices dependen de la versión y del compilador. Después de cambiar los operandos a 17 y 25, hay que volver a compilar y la salida debe ser El resultado es: 42. La portabilidad depende de una JVM que admita la versión de clase, además de las bibliotecas y dependencias requeridas. Encontrarás respuestas ampliadas en la solución orientativa de la práctica P14.

Fuentes y referencias