CodeGym /Cursos /Java Server /Tipos de errores: compile, build, runtime

Tipos de errores: compile, build, runtime

Java Server
Nivel 4, Lección 4
Disponible

1. «No funciona» — no es un diagnóstico

A estas alturas ya tenemos tres puntos de control honestos: classes, jar y run. Uno muestra si el código y los recursos compilan, otro — si el artefacto se empaqueta, el tercero — si la aplicación arranca. Ahora de todo esto se puede hacer el hábito más útil del día: primero entender en qué etapa se rompió todo.

Cuando empiezas a ensamblar y ejecutar el proyecto a través de Gradle, en algún momento ocurre lo inevitable: algo se rompe. Y la primera reacción es casi siempre la misma: «Gradle se rompió», «Java se rompió», «yo me rompí». Spoiler: normalmente se rompe un lugar concreto en un momento concreto — simplemente aún no estamos acostumbrados a distinguir esos momentos.

En esta lección aprendemos a hacernos la pregunta correcta: ¿en qué etapa se rompió? Eso acelera drásticamente la reparación. Porque arreglar «todo en general» es como reparar la bicicleta empezando por comprarse un nuevo garaje: la actividad es frenética, pero las ruedas siguen sin girar.

Tres etapas del proyecto: compilación → build → ejecución

Si simplificamos mucho, nuestro proyecto tiene tres «estados». Primero existe como fuentes (código Java + recursos). Luego se convierte en el resultado ensamblado (clases, recursos, jar). Y luego se convierte en un proceso en ejecución (aplicación lanzada).

Aquí es importante no confundirse con los términos: compile-time — es el momento en que el compilador Java intenta convertir tu .java en .class. build-time — es una etapa más amplia, cuando Gradle ejecuta las tareas del build (incluyendo compilación, procesamiento de recursos y empaquetado). runtime — es el momento en que la aplicación realmente arrancó y tu código ya se está ejecutando.

Para fijarlo, reduzcámoslo a una tabla. Es simple, pero muy útil (y sí, en la vida esa tablita ahorra horas).

Etapa Qué ocurre en esencia Con qué lo verificamos normalmente Dónde «arreglar»
Compile-time El código Java se compila a bytecode (.class) ./gradlew classes En el código Java: sintaxis, tipos, firmas
Build-time Gradle ejecuta las tareas del build: compilación, recursos, empaquetado, configuración de application ./gradlew jar /
./gradlew build
En build.gradle.kts, en la estructura del proyecto, en los recursos
Runtime Se lanza el proceso JVM, se llama a main(), se ejecuta tu código ./gradlew run En la lógica de la aplicación y en los recursos/config disponibles en la ejecución

Y otro pensamiento útil: los errores de compile-time casi siempre se manifiestan como una caída de Gradle en la tarea :compileJava. Es decir, técnicamente es un «error durante el build», pero por su sentido es un error de compilación del código Java. Por eso lo distinguimos como una clase separada: para que el cerebro no diga «otra vez Gradle», cuando en realidad el compilador está reportando honestamente que olvidamos un ;.

2. Compile-time: errores de compilación Java

Los errores de compile-time — son el tipo de errores más «amable». Son desagradables, pero honestos: la aplicación ni siquiera arrancará hasta que pongas el código en orden. El compilador no sabe «adivinar» tu intención, por eso reacciona de manera estricta. Y eso tiene un lado positivo: te enteras del problema de inmediato, y no dentro de 15 minutos, cuando dispare durante la ejecución del usuario (o tuya, pero ya después del café).

Normalmente los errores de compile-time aparecen por sintaxis, discrepancia de tipos, firmas de método incorrectas, imports ausentes, uso incorrecto de static, argumentos de llamada incorrectos. Importante: esos errores no dependen de si ejecutas desde la IDE o a través de Gradle — el compilador es el mismo, simplemente la IDE a menudo señala el problema antes.

Cómo se ve un error de compile-time

Tomemos el ejemplo más trivial: olvidaron el punto y coma. El código es pequeño, pero ya rompe toda la etapa de compilación.

package com.example.readlater;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // El compilador fallará aquí: al final de la instrucción hace falta un punto y coma
        System.out.println("ReadLater Starter") // <- olvidaron ;
    }
}

Si ejecutas:

./gradlew classes

Gradle llegará a :compileJava y se detendrá. En el mensaje habrá algo como `;` expected e indicación de la línea. Lo más valioso aquí — el número de línea y la causa concreta, no el grito general «BUILD FAILED».

Compile-time: «casi adiviné el tipo»

Una variante más real: esperas un número, pero trabajas con una cadena (o al revés). El compilador no juega a esos juegos.

package com.example.readlater;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // "8080" entre comillas — es String, no un número
        int port = "8080"; // compile-time: incompatible types
        System.out.println(port);
    }
}

En la cabeza a veces parece «bueno, 8080 es un número, ¿por qué...», pero para Java las comillas son de inmediato String.

Localización rápida del problema de compile-time

El truco del principiante que de forma inesperada se convierte en hábito profesional: verifica la compilación a través de classes cuando necesitas entender «¿el código compila en absoluto?»

./gradlew clean classes

¿Por qué es cómodo? Porque classes — es un objetivo bastante «estrecho». Compila Java y prepara los recursos, pero no empieza a empaquetar el jar ni ejecuta la aplicación. Si el problema está exactamente en el código, lo verás rápido y sin ruido innecesario.

3. Build-time: errores del build de Gradle

Los errores de build-time — es todo lo que se rompe en el proceso de ensamblado del proyecto como artefacto, pero no necesariamente en el código Java. Aquí normalmente los culpables son los ajustes en build.gradle.kts, la estructura del proyecto, la configuración incorrecta de los plugins, a veces los recursos, a veces simplemente erratas en el DSL.

Es el momento en que la persona escribe: «¡Tengo un error, pero en el código todo está bien!» — y a menudo tiene razón. Porque Gradle puede caer incluso antes de la compilación o ya después de ella, por ejemplo al empaquetar el jar o al intentar preparar el run.

Kotlin DSL: Gradle «no entiende» el script

Imagina que en el bloque application pusiste una errata en el nombre de la propiedad:

plugins {
    // Conectamos el soporte del lanzamiento a través de `run` y la configuración `application { ... }`
    application
}

application {
    // Error intencional en el ejemplo: la propiedad mainClasss no existe (errata)
    mainClasss = "com.example.readlater.ReadLaterApplication"
}

Eso no es código Java, sino build.gradle.kts. Si intentas ejecutar ./gradlew run o ./gradlew classes, Gradle puede caer incluso en la etapa de configuración del proyecto, es decir, antes de ejecutar las tareas. Por su sentido eso es un error de build-time, porque «la instrucción del build está rota».

Psicológicamente eso es traicionero: no cambiaste nada en Java, pero el proyecto dejó de ensamblarse. Pero si recordar que build.gradle.kts es también código (simplemente código para el build), todo se vuelve lógico.

El plugin no está conectado, pero el bloque ya existe

Otro caso clásico: escribes el bloque application { ... }, pero olvidaste conectar el plugin application (o accidentalmente lo eliminaste). Entonces Gradle no sabe qué es ese bloque, y el build cae.

plugins {
    // application  // <- alguien lo comentó, y empezó la diversión
}

application {
    // Este bloque requiere el plugin `application` conectado
    mainClass = "com.example.readlater.ReadLaterApplication"
}

Eso también es un problema de build-time: la estructura y la configuración del build no corresponden a lo que le pides a Gradle que haga.

Después de la compilación: recursos y empaquetado

A veces el código Java compila, pero el build de todas formas cae. Por ejemplo, si intentas ensamblar el jar y en el proyecto hay problemas extraños con los archivos, o editaste a mano algo que no debías tocar.

En la práctica lo verás así: :compileJava pasó, pero luego cayó en :processResources o :jar. Eso es una señal importante: el código compila, pero el sistema de build no pudo ensamblar el artefacto.

Localización rápida del problema de build-time

Aquí funciona el mismo principio del «comando mínimo»: si te interesa el archivo, usa jar.

./gradlew jar

Si classes pasa, pero jar cae — ya has reducido el área de búsqueda. Y eso es mucho más agradable que ejecutar build «por si acaso» y recibir un montón de salida.

4. Errores de runtime: cuando la aplicación ya se está ejecutando

Los errores de runtime — es lo que ocurre después del inicio de la aplicación, cuando el código Java ya se está ejecutando. El compilador estaba contento, Gradle ensambló todo, run lanzó la JVM — y entonces tu aplicación dice: «Ups». Normalmente es una excepción (exception) o un comportamiento incorrecto (por ejemplo, imprime algo distinto a lo esperado).

Para el principiante es el tipo de errores más desagradable, porque surge la sensación: «¡Pero si ensamblaba! ¡Significa que debería funcionar!» Ay, el ensamblado dice solo una cosa: «el código es formalmente correcto». Y el código correcto puede perfectamente ser incorrecto por su sentido. Java es disciplinada, pero todavía no telépata.

Ejemplo de runtime más simple: NullPointerException

Hagamos el ejemplo deliberadamente educativo: creaste una variable null y empezaste a usarla. El código compila perfectamente. Pero la ejecución cae.

package com.example.readlater;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // La variable es explícitamente null
        String value = null;

        // Cualquier llamada a método/campo de null llevará a NullPointerException
        System.out.println(value.length()); // runtime: NullPointerException
    }
}

Lanzamiento:

./gradlew run

Y verás el stack trace, donde estará escrito en qué línea ocurrió la caída. Eso ya es runtime, porque la JVM estaba ejecutando realmente tu código y llegó al lugar problemático.

Runtime y recursos: getResourceAsStream devuelve null

Eso es especialmente importante después de la lección sobre src/main/resources. getResourceAsStream() parece amistoso, pero tiene carácter: si no se encuentra el recurso, devuelve null, y no lanza una excepción. Y si de inmediato empiezas a leer de null, obtienes NPE.

Así se puede «accidentalmente» provocarse una caída de runtime, si el archivo banner.txt no está donde crees, o te equivocaste en el nombre.

package com.example.readlater;

import java.io.IOException;
import java.io.InputStream;

public class ReadLaterApplication {
    public static void main(String[] args) throws IOException {
        // Si no se encuentra el recurso, in será null
        try (InputStream in = ReadLaterApplication.class.getResourceAsStream("/banner.txt")) {
            // Aquí caerá con NPE si in == null
            System.out.println(in.available()); // runtime: NPE si in == null
        }
    }
}

Una variante un poco más madura, pero todavía muy sencilla — comprobar el null y caer con un mensaje comprensible, para que no busques «¿por qué NPE?», sino veas de inmediato «recurso no encontrado».

package com.example.readlater;

import java.io.InputStream;

public class ReadLaterApplication {
    public static void main(String[] args) {
        // Intentamos encontrar el recurso en el classpath
        InputStream in = ReadLaterApplication.class.getResourceAsStream("/banner.txt");

        // Comprobamos explícitamente «recurso no encontrado»
        if (in == null) {
            throw new IllegalStateException("Resource /banner.txt not found on classpath");
        }

        System.out.println("Banner found!"); // Banner found!
    }
}

Fíjate: sigue siendo un error de runtime (si no hay recurso), pero ahora explica el problema en términos humanos.

5. Diagnóstico a través de classes, jar, run

Cuando tienes tres tipos de errores, surge la pregunta natural: «Bien, ¿cómo entender rápidamente cuál tipo tengo ahora?» Aquí no ayuda la magia, sino una pequeña disciplina: verificar el proyecto con comandos estrechos y ver en qué tarea cayó todo.

En esta lección nos apoyamos deliberadamente en las tareas que ya conoces: classes, jar, run. Son tres puntos de control: «el código compila», «el artefacto se ensambla», «la aplicación arranca».

Dibujemos un diagrama de flujo simple que puedes tener en mente (o incluso al lado del proyecto, hasta que te acostumbres):

flowchart TD
    A["Ejecutaron ./gradlew run y todo se rompió"] --> B{"¿Cae ya en :compileJava?"}
    B -->|Sí| C["Compile-time: error en el código Java"]
    B -->|No| D{"¿Cae en la configuración de Gradle / build.gradle.kts?"}
    D -->|Sí| E["Build-time: error en el script/configuración del build"]
    D -->|No| F{"¿classes pasa pero jar cae?"}
    F -->|Sí| G["Build-time: problema de empaquetado/recursos/estructura"]
    F -->|No| H["Runtime: la aplicación arrancó y cayó en tu código"]

Si no quieres dibujar esquemas, puedes decirlo con una frase normal: primero me aseguro de que la compilación pasa (classes), luego — de que el empaquetado pasa (jar), y solo después verifico el lanzamiento (run). En qué punto se rompió, ahí investigo.

6. Tres mini-casos en ReadLater Starter

Ahora lo más útil: no solo teoría sobre los errores, sino tres roturas típicas que realmente ocurren a los principiantes, y cómo distinguirlas rápidamente. Las describiré como mini-historias, porque así el cerebro las recuerda más fácilmente.

Caso A: el proyecto no compila después de un pequeño cambio

Añadiste una línea a ReadLaterApplication y todo cayó. Ejecutas ./gradlew run y ves que la tarea :compileJava FAILED. Eso es casi siempre compile-time. No buscas el problema en los recursos, no tocas build/, no reinstalas Gradle (por favor, no reinstales Gradle). Abres el mensaje del compilador y corriges el código.

La causa más frecuente — un error sintáctico trivial o tipos incompatibles. Corrígelo — y todo marcha.

Caso B: no cambiaste el código Java, pero el build «cayó de repente»

No cambiaste nada en Java, pero decidiste «arreglar un poco build.gradle.kts». Después de eso cualquier lanzamiento cae, a veces incluso ./gradlew tasks se comporta extraño. Eso es build-time, y en estado puro: el script del build está roto.

Aquí ayuda un hábito simple: si cambiaste build.gradle.kts y después de eso todo se rompió, no busques el problema en el código Java. Deshaz el último cambio, busca dónde está la errata, y recuerda que Kotlin DSL es un DSL estricto, no le gustan los nombres «aproximadamente correctos».

Caso C: run arranca, pero la aplicación cae con una excepción

Eso es runtime. Y aquí es importante aprender a hacer dos cosas: primero, leer el stack trace de arriba a abajo hasta la primera línea de tu código, y segundo, no confundir la causa con el lugar de la caída.

Por ejemplo, si ves NullPointerException en la línea donde lees el recurso, la causa casi siempre no es «Java se rompió», sino «recurso no encontrado / variable null». Y eso ya se puede arreglar en tu código: comprobando el null, con la ruta correcta al recurso, con la ubicación correcta del archivo.

7. Leyendo la salida de Gradle

La salida de Gradle al principio parece «ruido de consola al estilo de ciencia ficción». Pero es bastante legible, si miras en el lugar correcto. El hábito importante es simple: Gradle casi siempre dice qué tarea cayó. Y la tarea — es una pista sobre en qué etapa ocurrió el problema.

Si ves algo como Task :compileJava FAILED, eso es un área de búsqueda. Si Task :processResources FAILED — otra. Si el error ocurrió antes de la lista de tareas, eso a menudo significa que Gradle no pudo ni siquiera configurar el proyecto, es decir, el problema está en build.gradle.kts.

A veces es útil ejecutar Gradle de forma que muestre más contexto. No como un «conjuro mágico», sino simplemente como una forma de obtener los detalles, si el texto estándar no es suficiente.

./gradlew run --stacktrace

Eso no te convierte en un «ingeniero de build avanzado». Es simplemente la petición: «muestra más detalle de dónde cayó». Pero lo importante es no ahogarse en los detalles. Primero busca: qué tarea, qué causa, qué archivo y línea. Y ya luego, si hace falta, mira más en profundidad.

Y otro consejo: nunca arregles el problema en la carpeta build/. Si algo se rompió, corriges los fuentes (src/main/java, src/main/resources) o el script de build (build.gradle.kts). La carpeta build/ es como la vajilla sucia después de cocinar: se puede lavar (clean), pero no tiene sentido «añadir ingredientes» ahí.

8. Errores típicos en el diagnóstico

Error n.º 1: llamar cualquier problema «error de Gradle».
Es muy fácil echarle la culpa a Gradle, porque el mensaje de error lo muestra él. Pero Gradle a menudo simplemente muestra honestamente que el compilador Java cayó o que tu programa cayó en runtime. Si ves :compileJava FAILED, hay que arreglar el código Java, no el «Gradle».

Error n.º 2: intentar curar el problema de runtime con clean.
clean es una tarea útil, pero no cura el NullPointerException ni añade el archivo banner.txt a la carpeta correcta. A veces clean ayuda a quitar la basura después de experimentos fallidos, pero más a menudo es simplemente una acción innecesaria. Si la aplicación cae en main(), limpiar build/ no convierte el null en cadena.

Error n.º 3: ejecutar siempre build, incluso cuando hace falta una verificación estrecha.
Cuando en pánico ejecutas el «comando más grande» (build), obtienes también la salida más grande, y ver la causa se vuelve más difícil. Muy a menudo basta con classes para entender que el código no compila, o jar para ver que el empaquetado se rompe, o run para atrapar la excepción de runtime.

Error n.º 4: arreglar los recursos editando build/resources/main.
Es uno de los escenarios más ofensivos: encontraste el archivo en build/resources/main, lo corregiste, todo funcionó... y luego se rompió de nuevo después del siguiente build. Porque build/ se regenera. La fuente de verdad — src/main/resources. Todo lo demás — resultado temporal.

Error n.º 5: confundir «no encontró la clase main» con «no compila».
Si Gradle no puede ejecutar mainClass, puede parecer aterrador, pero no siempre significa que el código Java esté roto. El código puede compilarse perfectamente, simplemente mainClass en build.gradle.kts está indicado incorrectamente: paquete equivocado o nombre incorrecto. Eso a menudo se percibe como runtime, pero por su sentido sigue siendo un problema de configuración del build.

1
Tarea
Java Server, nivel 4, lección 4
Bloqueada
Separar errores de compilación y de ejecución en una misma clase
Separar errores de compilación y de ejecución en una misma clase
1
Tarea
Java Server, nivel 4, lección 4
Bloqueada
Separar errores de compilación y de ejecución por diferentes causas
Separar errores de compilación y de ejecución por diferentes causas
1
Cuestionario/control
Tareas de Gradle, nivel 4, lección 4
No disponible
Tareas de Gradle
Compilación, recursos y ejecución
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION