Saltar a contenido

10. Depuración en IntelliJ IDEA

UD3 — Control de Flujo y Depuración · RA3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje

Aspecto Valor
Resultado de aprendizaje RA: 3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje.
Criterios de evaluación a los que contribuye esta sección CE 3.6 (prueba y depura los programas)
CE 3.7 (comenta y documenta el código).
Marco normativo RD 405/2023 · RD 450/2010 · RD 659/2023
Tecnología base Java 25 LTS (OpenJDK Temurin 25) — IntelliJ IDEA Community Edition
Horas estimadas ~3 h (de las 30 h de la UD3).

Requisitos previos (lo que ya sabes): todas las estructuras de control de UD3: condiciones booleanas y operadores (sección 1); if / else if / else y early return (sección 2); switch expression (sección 3); while y do-while (sección 4); arrays unidimensionales (sección 5); for clásico y utilidades de Arrays (sección 6); for-each (sección 7); bucles anidados, matrices int[][] y sentencias de salto break / continue / return (sección 8); try-catch-finally y manejo de excepciones predefinidas (sección 9). IntelliJ IDEA Community: ya sabes crear proyectos Maven, compilar con Shift+F10, y usar atajos básicos (Alt+Enter, Ctrl+Space, Ctrl+Alt+L). Lectura con IO.readln() y conversión con Integer.parseInt / Double.parseDouble.


10.1 ¿De qué va esta sección? — Dejar de adivinar, empezar a ver

Llegas a esta sección con un hábito que vamos a romper: cuando un programa no funciona, tu instinto es meter IO.println por todas partes para ver qué valor tienen las variables. Eso funciona, pero es lento, tedioso y propenso a errores:

  • Escribes IO.println("i = " + i) en 10 sitios.
  • Ejecutas, ves la salida, descubres que el problema está en otro sitio.
  • Borras los IO.println, añades 10 nuevos en otro sitio.
  • Repites. Y repites.

Hay una forma mucho mejor: el depurador (debugger) de IntelliJ IDEA. El depurador te permite detener el programa en cualquier punto, ver el valor de todas las variables en ese momento, ejecutar línea a línea y observar cómo cambian los valores. Es como poner el programa en cámara lenta y mirar dentro de su cerebro.

El depurador es la herramienta más importante del programador profesional. Cuando trabajes en una empresa, nadie te dejará meter IO.println en código de producción para buscar un bug. Te esperan con el depurador. Y esta sección es donde lo aprendes a usar bien.

Cubriremos, en este orden:

  1. Diferencia entre ejecutar y depurar: por qué el botón de depurar es distinto al de ejecutar.
  2. Breakpoints: puntos donde el programa se detiene.
  3. Controles de depuración: Step Over, Step Into, Step Out, Resume.
  4. Ventanas de depuración: Variables, Watches, Console, Frames.
  5. Evaluate Expression: evaluar expresiones en tiempo de ejecución.
  6. Breakpoints condicionales: detenerse solo cuando se cumple una condición.
  7. Flujo de trabajo para encontrar un bug: el método profesional.
  8. Errores comunes en la depuración.
  9. Ejemplos resueltos: depurar programas con bugs.

Analogía del médico y la radiografía

Sin depurador, depurar es como un médico que diagnostica a ciegas: el paciente le dice "me duele aquí" y el médico receta algo a ver si funciona. Con depurador, es como tener una radiografía: el médico ve exactamente qué hay dentro, dónde está el problema, y puede actuar con precisión. El depurador es tu radiografía del programa.

Vamos a empezar por lo más básico: la diferencia entre ejecutar y depurar.


10.2 Ejecutar vs. Depurar: dos botones distintos

10.2.1 Ejecutar (Run)

Hasta ahora has usado el botón Run (el triángulo verde) o el atajo Shift+F10 para ejecutar tus programas. Cuando ejecutas, el programa corre de principio a fin sin parar, y solo ves la salida en la consola. Si hay un bug, ves el síntoma (salida incorrecta o excepción) pero no puedes ver qué estaba pasando dentro en el momento del error.

10.2.2 Depurar (Debug)

El botón Debug (el icono del escarabajo verde, "bug") o el atajo Shift+F9 hace lo mismo que Run, pero con superpoderes: el programa se ejecuta línea a línea, se puede detener en los breakpoints que pongas, y puedes inspeccionar variables en cada momento.

Característica Run (Shift+F10) Debug (Shift+F9)
Velocidad Rápido. Un poco más lento (la JVM se prepara para depurar).
Detención No se detiene (salvo excepciones o fin). Se detiene en breakpoints.
Ver variables Solo con IO.println manual. En la ventana de Variables, automáticamente.
Ejecutar línea a línea No. Sí (Step Over, Step Into...).
Uso Para probar que el programa funciona. Para investigar por qué no funciona.

10.2.3 Cuándo usar cada uno

  • Run: cuando el programa ya funciona y quieres ejecutarlo para ver el resultado final. O cuando estás haciendo pruebas rápidas y no necesitas inspeccionar el interior.
  • Debug: cuando el programa no funciona como esperas y necesitas investigar por qué. O cuando quieres entender cómo fluye la ejecución de un código que no escribiste tú.

Regla del profesor:

Si el programa funciona, usa Run. Si no funciona, usa Debug. Si quieres entender cómo funciona un código, usa Debug.


10.3 Breakpoints: detener el programa

10.3.1 Qué es un breakpoint

Un breakpoint (punto de interrupción) es una marca que pones en una línea de código. Cuando depuras el programa y la ejecución llega a esa línea, el programa se detiene y te cede el control. A partir de ahí, puedes:

  • Ver el valor de todas las variables en ese momento.
  • Ejecutar línea a línea.
  • Continuar la ejecución hasta el siguiente breakpoint.

10.3.2 Cómo poner un breakpoint

La forma más simple: haz clic en el margen izquierdo del editor, a la izquierda del número de línea. Aparecerá un punto rojo:

Use mouse to pan and zoom
flowchart LR
    A["Clic en margen izquierdo"] --> B["Aparece punto rojo ●"]
    B --> C["Breakpoint activo"]
    C --> D["El programa se detendrá aquí al depurar"]

Para quitarlo: vuelve a hacer clic en el mismo sitio y el punto rojo desaparece.

Atajo de teclado: Ctrl+F8 (Windows/Linux) o Cmd+F8 (Mac) sobre la línea donde quieres poner el breakpoint.

10.3.3 Ejemplo: detener el programa en una línea

public class DemoBreakpoint {

    public static void main(String[] args) {

        int[] numeros = {10, 20, 30, 40, 50};
        int suma = 0;

        for (int i = 0; i < numeros.length; i++) {   // ← Pon un breakpoint aquí
            suma = suma + numeros[i];
        }

        IO.println("La suma es: " + suma);
    }
}

Si pones un breakpoint en la línea del for y pulsas Shift+F9 (Debug), el programa se detendrá en la primera iteración del bucle, justo antes de ejecutar suma = suma + numeros[i]. Verás:

  • La variable i con valor 0.
  • La variable suma con valor 0.
  • El array numeros con sus 5 valores.

10.3.4 Múltiples breakpoints

Puedes poner tantos breakpoints como quieras. El programa se detiene en el primero que encuentre. Cuando lo reanudes (con F9), corre hasta el siguiente breakpoint.

10.3.5 Breakpoint en una línea con varias sentencias

Un breakpoint se pone en una línea de código. Si la línea tiene varias sentencias (poco habitual en Java), el breakpoint se activa al inicio de la línea.

10.3.6 Lo que NO es un breakpoint

  • No es un IO.println: no imprime nada en la consola.
  • No es un if: no tiene condición (aunque los breakpoints condicionales sí la tienen; ver 10.7).
  • No detiene el programa cuando lo ejecutas con Run (Shift+F10): solo funciona con Debug (Shift+F9).

10.4 Controles de depuración: ejecutar línea a línea

Una vez que el programa está detenido en un breakpoint, tienes varios controles para ejecutarlo línea a línea. Son los más importantes del depurador y los que vas a usar siempre.

10.4.1 Step Over (F8)

Ejecuta la línea actual y avanza a la siguiente. Si la línea actual contiene una llamada a un método, no entra dentro del método: lo ejecuta entero y se detiene en la línea siguiente.

int resultado = calcularPrecio(cantidad, descuento);   // ← Estás aquí
IO.println("Precio: " + resultado);                   // Step Over (F8) avanza aquí

Step Over es el control que vas a usar el 90 % de las veces. Te permite avanzar línea a línea sin perderte dentro de métodos que ya funcionan.

10.4.2 Step Into (F7)

Entra dentro del método que se está llamando en la línea actual. Si la línea tiene una llamada a un método, en vez de ejecutarlo entero, se detiene en la primera línea del método.

int resultado = calcularPrecio(cantidad, descuento);   // ← Estás aquí
// Step Into (F7) entra dentro de calcularPrecio() y se detiene en su primera línea

Step Into lo usas cuando sospechas que el bug está dentro del método que se llama en esta línea. Si el método ya funciona (p. ej., Integer.parseInt o IO.readln), no uses Step Into: te meterás en código del API de Java que no te interesa.

10.4.3 Step Out (Shift+F8)

Sale del método actual y vuelve a quien lo llamó. Si estás dentro de un método y ya has visto lo que querías, Step Out te lleva de vuelta al llamador, en la línea siguiente a la que te llamó.

1
2
3
4
// Dentro de calcularPrecio():
// ... ya has visto lo que querías ...
return precio;   // ← Estás aquí
// Step Out (Shift+F8) vuelve al llamador, en la línea después de la llamada

10.4.4 Resume (F9)

Continúa la ejecución hasta el siguiente breakpoint (o hasta el fin del programa si no hay más breakpoints). Útil cuando ya has inspeccionado lo que querías y quieres dejar correr el programa hasta la próxima parada.

10.4.5 Stop (Ctrl+F2)

Detiene el programa y la depuración. Cierra la sesión de depuración y vuelve al editor. Útil cuando ya has encontrado el bug y quieres corregir el código.

10.4.6 Tabla resumen de controles

Control Atajo (Win/Linux) Atajo (Mac) Qué hace
Step Over F8 F8 Ejecuta la línea actual, no entra en métodos.
Step Into F7 F7 Entra dentro del método de la línea actual.
Step Out Shift+F8 Shift+F8 Sale del método actual y vuelve al llamador.
Resume F9 F9 Continúa hasta el siguiente breakpoint.
Stop Ctrl+F2 Cmd+F2 Detiene el programa y la depuración.

10.4.7 Diagrama de los controles

Use mouse to pan and zoom
flowchart TD
    Detenido["Programa detenido en breakpoint"] --> Pregunta{"¿Qué quieres hacer?"}
    Pregunta -->|"Avanzar una línea\n(sin entrar en métodos)"| Over["Step Over (F8)"]
    Pregunta -->|"Entrar en el método\nde la línea actual"| Into["Step Into (F7)"]
    Pregunta -->|"Salir del método actual\ny volver al llamador"| Out["Step Out (Shift+F8)"]
    Pregunta -->|"Continuar hasta el\nsiguiente breakpoint"| Resume["Resume (F9)"]
    Pregunta -->|"Detener todo"| Stop["Stop (Ctrl+F2)"]
    Over --> Detenido2["Programa detenido en la siguiente línea"]
    Into --> Detenido3["Programa detenido en la primera línea del método"]
    Out --> Detenido4["Programa detenido en el llamador"]
    Resume --> Detenido5["Programa corre hasta el próximo breakpoint"]
    Stop --> Fin([Fin de la depuración])
    Detenido2 --> Detenido
    Detenido3 --> Detenido
    Detenido4 --> Detenido
    Detenido5 --> Detenido

10.5 Ventanas de depuración

Cuando el programa está detenido en un breakpoint, IntelliJ te muestra varias ventanas con información. Las más importantes son:

10.5.1 Ventana Variables

Muestra todas las variables accesibles en el punto donde está detenido el programa, con sus valores actuales. Es la ventana que más vas a mirar.

1
2
3
4
5
Variables
├── numeros: int[5] = {10, 20, 30, 40, 50}
├── i: int = 0
├── suma: int = 0
└── args: String[0] = {}

Para los arrays, puedes expandirlos (clic en la flecha > al lado del nombre) y ver cada elemento:

1
2
3
4
5
6
7
8
9
Variables
├── numeros: int[5] = {10, 20, 30, 40, 50}
│   ├── [0]: 10
│   ├── [1]: 20
│   ├── [2]: 30
│   ├── [3]: 40
│   └── [4]: 50
├── i: int = 0
└── suma: int = 0

10.5.2 Ventana Watches

Te permite añadir expresiones personalizadas que quieres vigilar. Por ejemplo, puedes añadir numeros[i] o i < numeros.length y ver su valor en cada paso.

Para añadir un watch: clic en el botón + de la ventana Watches (o Alt+Insert sobre ella) y escribe la expresión.

Ventana Qué muestra Cuándo usarla
Variables Todas las variables accesibles. Siempre: para ver el estado del programa.
Watches Expresiones que tú eliges. Cuando quieres vigilar una expresión concreta (p. ej., numeros[i] dentro de un bucle).

10.5.3 Ventana Console

La consola normal: muestra la salida del programa (IO.println) y la entrada del usuario (IO.readln). Mientras depuras, puedes interactuar con el programa por la consola igual que cuando lo ejecutas con Run.

10.5.4 Ventana Frames

Muestra la pila de llamadas (call stack): la lista de métodos que están en ejecución, desde el más interno hasta main. Útil para entender cómo llegaste al punto donde estás.

Frames
├── main(String[] args)    DemoBreakpoint.java:7

En UD3 todos tus programas tienen main solo, así que la pila es corta. Cuando veas métodos propios en UD4, la pila será más larga y esta ventana será más útil.


10.6 Evaluate Expression: evaluar en tiempo de ejecución

10.6.1 Qué es

Evaluate Expression (Alt+F8) te permite escribir cualquier expresión Java y ver su valor en el punto donde está detenido el programa. Es como un mini-interprete de Java que tiene acceso a todas las variables del programa en ese momento.

10.6.2 Ejemplo

El programa está detenido en la línea suma = suma + numeros[i]; con i = 2 y suma = 30. Pulsas Alt+F8 y escribes:

1
2
3
4
numeros[i]              // → 30 (el elemento actual)
numeros[i] * 2          // → 60
i < numeros.length      // → true
suma + numeros[i]       // → 60 (lo que valdrá suma después de esta línea)

Evaluate Expression es muy potente para investigar: puedes probar expresiones sin modificar el código, ver qué pasaría si los valores fuesen distintos, etc.

10.6.3 Cuándo usarlo

  • Para ver el valor de una expresión compleja que no está como variable.
  • Para probar qué pasaría con distintos valores sin modificar el código.
  • Para comprobar el resultado de una operación antes de ejecutar la línea.

10.7 Breakpoints condicionales

10.7.1 Qué son

Un breakpoint condicional se detiene solo cuando se cumple una condición. En vez de parar en cada iteración de un bucle (que para 1000 iteraciones sería impresentable), puedes parar solo cuando i == 500 o cuando numeros[i] < 0.

10.7.2 Cómo poner uno

  1. Pon un breakpoint normal (clic en el margen).
  2. Haz clic derecho sobre el punto rojo.
  3. Aparece un diálogo. En el campo Condition, escribe una expresión booleana:
i == 500
  1. El punto rojo se convierte en un punto rojo con un signo = dentro, indicando que es condicional.

Ahora el programa solo se detiene cuando i == 500. Las demás iteraciones pasan de largo.

10.7.3 Ejemplos de condiciones

Condición Cuándo se detiene
i == 50 En la iteración 50 del bucle.
numeros[i] < 0 Cuando encuentra un número negativo.
suma > 100 Cuando la suma acumulada supera 100.
nombre.equals("Ana") Cuando el nombre es "Ana".
i % 100 == 0 Cada 100 iteraciones (en bucles grandes).

10.7.4 Cuándo usarlos

Los breakpoints condicionales son esenciales para depurar bucles con muchas iteraciones. Sin ellos, tendrías que pulsar F8 (Step Over) cientos de veces para llegar a la iteración que te interesa. Con un breakpoint condicional, el programa corre solo y se detiene justo en la iteración que quieres.


10.8 Flujo de trabajo para encontrar un bug

Vamos a ver el método profesional para encontrar un bug. Sigue estos pasos en orden:

10.8.1 Paso 1: Reproducir el bug

Antes de depurar, asegúrate de que puedes reproducir el error. Si el bug no aparece siempre, necesitas saber qué entrada lo provoca. Anota la entrada que produce el error.

10.8.2 Paso 2: Localizar la zona sospechosa

Lee el código y piensa: ¿dónde puede estar el error? No pongas breakpoints por todo el programa: pon breakpoints en la zona sospechosa. Si es un bucle, pon un breakpoint en la primera línea del bucle. Si es un if, pon un breakpoint en la condición.

10.8.3 Paso 3: Depurar (Shift+F9)

Pulsa Shift+F9 (Debug). El programa corre hasta el primer breakpoint y se detiene.

10.8.4 Paso 4: Inspeccionar variables

Mira la ventana Variables. ¿Tienen los valores que esperabas? Si una variable tiene un valor inesperado, ya sabes por dónde empezar a investigar.

10.8.5 Paso 5: Ejecutar línea a línea (F8)

Pulsa F8 (Step Over) repetidamente. Observa cómo cambian las variables en cada línea. Cuando una variable cambia a un valor inesperado, esa línea es la culpable.

10.8.6 Paso 6: Si la línea llama a un método, entrar con F7

Si la línea que cambia el valor de forma inesperada contiene una llamada a un método, pulsa F7 (Step Into) para entrar dentro y ver qué pasa.

10.8.7 Paso 7: Corregir y probar

Cuando hayas encontrado el error, detén la depuración (Ctrl+F2), corrige el código, y vuelve a ejecutar (Shift+F10 o Shift+F9) para verificar que el bug está resuelto.

10.8.8 Diagrama del flujo de trabajo

Use mouse to pan and zoom
flowchart TD
    A["1. Reproducir el bug"] --> B["2. Localizar zona sospechosa"]
    B --> C["3. Poner breakpoints"]
    C --> D["4. Depurar (Shift+F9)"]
    D --> E["5. Inspeccionar variables"]
    E --> F["6. Step Over (F8) línea a línea"]
    F --> G{"¿Valor inesperado?"}
    G -->|"Sí"| H["7. Esa línea es la culpable"]
    G -->|"No"| F
    H --> I{"¿Llama a un método?"}
    I -->|"Sí"| J["Step Into (F7) y repite 5-6"]
    I -->|"No"| K["8. Detener (Ctrl+F2)"]
    J --> K
    K --> L["9. Corregir el código"]
    L --> M["10. Ejecutar para verificar"]

10.9 Trampas comunes en la depuración

10.9.1 Trampa 1: Step Into en métodos del API

int n = Integer.parseInt(entrada);   // ← Step Into aquí te lleva al código fuente de Integer.parseInt()

Si haces Step Into en Integer.parseInt, IO.readln o cualquier método del API de Java, te meterás en código que no es tuyo y que no te interesa. Usa Step Over (F8) para métodos del API.

Solución: reserva Step Into para métodos que tú has escrito (en UD4 verás cómo crearlos).

10.9.2 Trampa 2: Olvidar quitar breakpoints

Si dejas breakpoints puestos y ejecutas con Run (Shift+F10), no pasa nada: el programa corre normal. Pero si ejecutas con Debug (Shift+F9), se detendrá en los breakpoints viejos y no entenderás por qué.

Solución: cuando termines de depurar, revisa los breakpoints (clic en el punto rojo para quitarlo) o usa el botón Mute Breakpoints (el icono con dos rayas grises) para desactivarlos todos temporalmente.

10.9.3 Trampa 3: Depurar sin saber qué buscas

Depurar sin un plan es como buscar una aguja en un pajar a ciegas. Antes de pulsar Shift+F9, piensa: "¿Qué valor espero en esta variable? ¿Qué valor veo?" Si no sabes qué esperas, no sabrás si lo que ves es correcto o no.

Solución: antes de depurar, escribe en un papel lo que esperas que valga cada variable. Compara con lo que el depurador te muestra.

10.9.4 Trampa 4: Bucles con muchas iteraciones

Si pones un breakpoint normal dentro de un bucle de 1000 iteraciones, tendrás que pulsar F9 (Resume) 1000 veces para llegar al final. Impracticable.

Solución: usa breakpoints condicionales (i == 500) para detener solo en la iteración que te interesa.

10.9.5 Trampa 5: Confundir Step Over y Step Into

Quieres... Usas...
Avanzar a la siguiente línea sin entrar en métodos. Step Over (F8)
Entrar dentro de un método que tú escribiste. Step Into (F7)
Salir del método actual. Step Out (Shift+F8)

Memoriza esta tabla. Es el 90 % de lo que necesitas saber del depurador.

10.9.6 Resumen de trampas

Trampa Síntoma Solución
Step Into en API Te pierdes en código de Java. Usar Step Over (F8) para métodos del API.
Olvidar breakpoints El programa se detiene sin razón aparente. Revisar o silenciar breakpoints al terminar.
Depurar sin plan No sabes si lo que ves es correcto. Escribir lo que esperas antes de depurar.
Bucles largos Muchas iteraciones, imposible parar. Breakpoints condicionales.
Confundir Step Over/Into Entras donde no querías o no entras donde querías. Memorizar la tabla de 10.9.5.

10.10 Ejemplos resueltos: depurar programas con bugs

Vamos a ver tres programas con bugs y cómo encontrarlos con el depurador.

10.10.1 Ejemplo 1: suma incorrecta

Programa con bug:

public class SumaBuggy {

    public static void main(String[] args) {

        int[] numeros = {10, 20, 30, 40, 50};
        int suma = 0;

        for (int i = 0; i <= numeros.length; i++) {   // BUG: <= en vez de <
            suma = suma + numeros[i];
        }

        IO.println("La suma es: " + suma);
    }
}

Síntoma: el programa se cae con ArrayIndexOutOfBoundsException en algún momento.

Depuración:

  1. Reproducir: ejecutar con Shift+F10 y ver el error en la consola.
  2. Localizar: el error está en el bucle. Pon un breakpoint en la línea suma = suma + numeros[i];.
  3. Depurar (Shift+F9): el programa se detiene en la primera iteración. Variables: i = 0, suma = 0, numeros = {10, 20, 30, 40, 50}. Todo correcto.
  4. Step Over (F8) varias veces: i sube a 1, 2, 3, 4. suma sube a 10, 30, 60, 100, 150. Todo correcto hasta aquí.
  5. Step Over una vez más: i sube a 5. La condición i <= numeros.length es 5 <= 5 = true, así que entra al cuerpo. numeros[5] → ¡ArrayIndexOutOfBoundsException!.
  6. Encontrado: el bug es <= en vez de < en el for. La trampa nº 1 con arrays.
  7. Corregir: cambiar i <= numeros.length por i < numeros.length.

10.10.2 Ejemplo 2: máximo incorrecto

Programa con bug:

public class MaximoBuggy {

    public static void main(String[] args) {

        int[] numeros = {5, 8, 3, 9, 2};
        int max = 0;   // BUG: 0 en vez de numeros[0]

        for (int i = 0; i < numeros.length; i++) {
            if (numeros[i] > max) {
                max = numeros[i];
            }
        }

        IO.println("El máximo es: " + max);
    }
}

Síntoma: el programa dice "El máximo es: 9" — parece correcto, pero si cambiamos el array a {-5, -8, -3, -9, -2}, diría "El máximo es: 0" (incorrecto, debería ser -2).

Depuración:

  1. Reproducir: cambiar el array a {-5, -8, -3, -9, -2} y ejecutar. El resultado es 0, que es incorrecto.
  2. Localizar: el bug está en la inicialización de max. Pon un breakpoint en int max = 0;.
  3. Depurar (Shift+F9): Variables: max = 0, numeros = {-5, -8, -3, -9, -2}.
  4. Step Over (F8) en el bucle: en cada iteración, numeros[i] > max evalúa -5 > 0, -8 > 0, etc. → todo false. max nunca cambia, se queda en 0.
  5. Encontrado: el bug es max = 0 en vez de max = numeros[0]. Con 0 como inicial, ningún número negativo supera 0.
  6. Corregir: cambiar int max = 0; por int max = numeros[0]; (y empezar el bucle en i = 1).

10.10.3 Ejemplo 3: bucle que no termina

Programa con bug:

public class BucleInfinito {

    public static void main(String[] args) {

        int i = 0;
        int suma = 0;

        while (i < 10) {
            suma = suma + i;
            // BUG: falta i++
        }

        IO.println("Suma: " + suma);
    }
}

Síntoma: el programa se queda colgado. Nunca imprime nada.

Depuración:

  1. Reproducir: ejecutar con Shift+F10 y ver que se cuelga. Detener con Ctrl+F2 (o el botón Stop).
  2. Localizar: el bug está en el bucle. Pon un breakpoint en la línea suma = suma + i;.
  3. Depurar (Shift+F9): el programa se detiene. Variables: i = 0, suma = 0.
  4. Step Over (F8): suma = 0 + 0 = 0. La línea siguiente es el } del while, que vuelve arriba.
  5. Step Over otra vez: i sigue siendo 0. suma sigue siendo 0. El bucle repite sin avanzar.
  6. Encontrado: falta i++ dentro del bucle. i nunca cambia, la condición i < 10 siempre es true.
  7. Corregir: añadir i++; dentro del bucle.

10.11 Buenas prácticas de depuración

10.11.1 Las 8 reglas del depurador profesional

  1. Reproduce el bug antes de depurar: sin reproducción, no hay depuración.
  2. Ten un plan: escribe qué esperas que valgan las variables antes de empezar.
  3. Pon breakpoints en la zona sospechosa, no por todo el programa.
  4. Step Over (F8) por defecto, Step Into (F7) solo para tus métodos.
  5. Breakpoints condicionales para bucles con muchas iteraciones.
  6. Evaluate Expression (Alt+F8) para probar expresiones sin modificar el código.
  7. Limpia los breakpoints cuando termines (o silencia con Mute Breakpoints).
  8. Corrige y vuelve a probar: un bug corregido no es un bug resuelto hasta que lo has verificado.

10.11.2 Cuándo usar Run vs. Debug

Situación Usa
El programa funciona y quieres ver el resultado. Run (Shift+F10)
El programa no funciona y quieres investigar. Debug (Shift+F9)
Quieres entender cómo fluye un código. Debug (Shift+F9)
Quiero ver si una corrección funciona. Run (Shift+F10), o Debug si sigue fallando.
Tengo un bucle infinito. Debug (Shift+F9) con un breakpoint en el bucle.

10.11.3 No abuses del depurador

El depurador es potente, pero no es la única herramienta. A veces lo mejor es:

  • Leer el código con calma: muchos bugs se ven a simple vista si lees despacio.
  • Imprimir con IO.println: para bugs simples, un IO.println("llegué aquí, i = " + i) es suficiente.
  • Pensar antes de pulsar teclas: el 50 % de los bugs se resuelven pensando, no depurando.

Regla del profesor:

El depurador es como un microscopio: potente, pero no lo uses para todo. Si el bug es "se me olvidó un ;", el compilador te lo dice. Si es "no inicialicé una variable", leer el código basta. El depurador brilla cuando el bug es lógico (el programa hace algo distinto a lo que esperas) y no sabes dónde está.


10.12 Resumen y mapa conceptual

10.12.1 Mapa conceptual

Use mouse to pan and zoom
mindmap
  root((Sección 10 - Depuración IntelliJ))
    Ejecutar vs Depurar
      Run Shift+F10
      Debug Shift+F9
      Run para probar
      Debug para investigar
    Breakpoints
      Clic en margen izquierdo
      Punto rojo
      Múltiples breakpoints
      Condicionales con condition
    Controles
      Step Over F8 no entra en métodos
      Step Into F7 entra en métodos
      Step Out Shift+F8 sale del método
      Resume F9 continúa
      Stop Ctrl+F2 detiene
    Ventanas
      Variables todas las variables
      Watches expresiones personalizadas
      Console salida y entrada
      Frames pila de llamadas
    Evaluate Expression
      Alt+F8
      Probar expresiones
      Sin modificar el código
    Flujo de trabajo
      1 reproducir el bug
      2 localizar zona
      3 poner breakpoints
      4 depurar
      5 inspeccionar
      6 step over línea a línea
      7 corregir
    Trampas
      Step Into en API
      Olvidar breakpoints
      Depurar sin plan
      Bucles largos
      Confundir Over e Into

10.12.2 Las 10 ideas que no debes olvidar

  1. Run (Shift+F10) ejecuta sin parar; Debug (Shift+F9) detiene en breakpoints.
  2. Un breakpoint se pone con clic en el margen izquierdo: aparece un punto rojo.
  3. Step Over (F8) es el control que más vas a usar: avanza una línea sin entrar en métodos.
  4. Step Into (F7) entra dentro de un método que tú escribiste. No lo uses en métodos del API.
  5. Step Out (Shift+F8) sale del método actual y vuelve al llamador.
  6. Resume (F9) continúa hasta el siguiente breakpoint.
  7. La ventana Variables muestra todas las variables accesibles; la ventana Watches muestra expresiones que tú eliges.
  8. Evaluate Expression (Alt+F8) te permite evaluar cualquier expresión Java en el punto de parada.
  9. Breakpoints condicionales se detienen solo cuando se cumple una condición (i == 500, numeros[i] < 0).
  10. El flujo de trabajo es: reproducir → localizar → depurar → inspeccionar → step over → encontrar → corregir → verificar.

10.12.3 Glosario rápido

Término Significado
Depuración (debugging) Proceso de encontrar y corregir errores en un programa.
Depurador (debugger) Herramienta que permite ejecutar un programa paso a paso e inspeccionar su estado.
Breakpoint Punto de interrupción: marca en una línea donde el programa se detiene al depurar.
Breakpoint condicional Breakpoint que se activa solo cuando se cumple una condición.
Step Over (F8) Ejecuta la línea actual sin entrar en métodos.
Step Into (F7) Entra dentro del método de la línea actual.
Step Out (Shift+F8) Sale del método actual y vuelve al llamador.
Resume (F9) Continúa la ejecución hasta el siguiente breakpoint.
Stop (Ctrl+F2) Detiene el programa y la depuración.
Ventana Variables Muestra todas las variables accesibles con sus valores actuales.
Ventana Watches Muestra expresiones personalizadas que tú eliges vigilar.
Ventana Frames Muestra la pila de llamadas (qué método llamó a qué).
Evaluate Expression (Alt+F8) Permite evaluar cualquier expresión Java en el punto de parada.
Run (Shift+F10) Ejecuta el programa sin depuración.
Debug (Shift+F9) Ejecuta el programa con depuración (se detiene en breakpoints).
Mute Breakpoints Desactiva todos los breakpoints temporalmente.
Bug Error en el programa que hace que se comporte de forma incorrecta.
Call stack (pila de llamadas) Lista de métodos en ejecución, desde el más interno hasta main.

10.13 Actividades propuestas

Cinco actividades graduadas. Hazlas en orden. Las soluciones se publicarán en Aules.

10.13.1 Actividad 1 — Depurar el "suma incorrecta"

Objetivo: practicar el flujo de depuración con un bug de off-by-one.

Enunciado: Crea el siguiente programa (con un bug intencionado):

public class SumaBuggy {

    public static void main(String[] args) {

        int[] numeros = {10, 20, 30, 40, 50};
        int suma = 0;

        for (int i = 0; i <= numeros.length; i++) {
            suma = suma + numeros[i];
        }

        IO.println("La suma es: " + suma);
    }
}

Tareas:

  1. Ejecuta con Shift+F10 y observa el error en la consola.
  2. Pon un breakpoint en la línea suma = suma + numeros[i];.
  3. Depura con Shift+F9.
  4. Usa F8 (Step Over) repetidamente y observa cómo cambian i y suma.
  5. Identifica en qué iteración salta el error y por qué.
  6. Corrige el bug y verifica con Shift+F10.

Entrega: archivo SumaBuggy.java (corregido) en el paquete jams.programacion.ud3, con un comentario al final explicando qué era el bug y cómo lo encontraste con el depurador.

Criterios evaluados: CE 3.6, CE 3.7.

10.13.2 Actividad 2 — Depurar el "máximo incorrecto"

Objetivo: practicar la inspección de variables para encontrar un bug lógico.

Enunciado: Crea el siguiente programa (con un bug intencionado):

public class MaximoBuggy {

    public static void main(String[] args) {

        int[] numeros = {-5, -8, -3, -9, -2};
        int max = 0;

        for (int i = 0; i < numeros.length; i++) {
            if (numeros[i] > max) {
                max = numeros[i];
            }
        }

        IO.println("El máximo es: " + max);
    }
}

Tareas:

  1. Ejecuta con Shift+F10 y observa el resultado (dirá "El máximo es: 0", que es incorrecto).
  2. Pon un breakpoint en la línea if (numeros[i] > max).
  3. Depura con Shift+F9.
  4. Usa F8 (Step Over) y observa el valor de numeros[i] y max en cada iteración.
  5. Usa Alt+F8 (Evaluate Expression) para evaluar numeros[i] > max en cada paso. ¿Por qué siempre es false?
  6. Identifica el bug y corrídelo.
  7. Verifica con Shift+F10.

Entrega: archivo MaximoBuggy.java (corregido) en el paquete jams.programacion.ud3, con un comentario al final explicando el bug.

Criterios evaluados: CE 3.6, CE 3.7.

10.13.3 Actividad 3 — Depurar con breakpoint condicional

Objetivo: practicar breakpoints condicionales en un bucle largo.

Enunciado: Crea el siguiente programa, que suma los números del 1 al 1000 pero tiene un bug en la iteración 743 (la suma salta de 276.281 a 277.281 en vez de a 276.282):

public class SumaMilBuggy {

    public static void main(String[] args) {

        int suma = 0;
        for (int i = 1; i <= 1000; i++) {
            if (i == 743) {
                suma = suma + i + 1000;   // BUG: +1000 extra
            } else {
                suma = suma + i;
            }
        }
        IO.println("Suma del 1 al 1000: " + suma);
        IO.println("Debería ser: " + (1000 * 1001 / 2));
    }
}

Tareas:

  1. Ejecuta con Shift+F10 y observa que el resultado no coincide con el esperado.
  2. Pon un breakpoint condicional en la línea suma = suma + i + 1000; con la condición i == 743.
  3. Depura con Shift+F9. El programa debería detenerse solo en la iteración 743.
  4. Inspecciona i, suma antes y después de la línea.
  5. Identifica el bug (el +1000 extra) y corrídelo.
  6. Verifica con Shift+F10.

Entrega: archivo SumaMilBuggy.java (corregido) en el paquete jams.programacion.ud3, con un comentario al final explicando cómo usaste el breakpoint condicional.

Criterios evaluados: CE 3.6, CE 3.7.

10.13.4 Actividad 4 — Depurar un bucle infinito

Objetivo: practicar la depuración de un bucle que no termina.

Enunciado: Crea el siguiente programa (con un bug intencionado):

public class BucleInfinito {

    public static void main(String[] args) {

        int i = 0;
        int suma = 0;

        while (i < 10) {
            suma = suma + i;
            // Falta algo aquí...
        }

        IO.println("Suma: " + suma);
    }
}

Tareas:

  1. Ejecuta con Shift+F10 y observa que el programa se cuelga. Detén con Ctrl+F2.
  2. Pon un breakpoint en la línea suma = suma + i;.
  3. Depura con Shift+F9.
  4. Usa F8 (Step Over) varias veces y observa qué variable no cambia.
  5. Identifica qué falta (el i++) y corrídelo.
  6. Verifica con Shift+F10.

Entrega: archivo BucleInfinito.java (corregido) en el paquete jams.programacion.ud3, con un comentario al final explicando el bug.

Criterios evaluados: CE 3.6, CE 3.7.

10.13.5 Actividad 5 — Cuestionario de repaso

Responde por escrito:

  1. ¿Cuál es la diferencia entre Run (Shift+F10) y Debug (Shift+F9)?
  2. ¿Cómo pones un breakpoint? ¿Cómo lo quitas?
  3. ¿Qué hace Step Over (F8)? ¿En qué se diferencia de Step Into (F7)?
  4. ¿Cuándo usarías Step Into (F7) y cuándo NO?
  5. ¿Qué hace Step Out (Shift+F8)?
  6. ¿Qué hace Resume (F9)?
  7. ¿Qué muestra la ventana Variables? ¿Y la ventana Watches?
  8. ¿Para qué sirve Evaluate Expression (Alt+F8)? Pon un ejemplo de uso.
  9. ¿Qué es un breakpoint condicional y por qué es útil en bucles largos?
  10. Describe los 7 pasos del flujo de trabajo para encontrar un bug.
  11. ¿Por qué no debes hacer Step Into en Integer.parseInt o IO.readln?
  12. ¿Qué haces cuando terminas de depurar: dejas los breakpoints puestos o los quitas? ¿Por qué?

Entrega: archivo cuestionario_seccion10.md con tus respuestas.

Criterios evaluados: CE 3.6, CE 3.7.


10.14 Lo que NO hemos visto (y dónde verlo)

Esta sección te ha enseñado la depuración básica con IntelliJ IDEA: breakpoints, controles Step Over/Into/Out/Resume, ventanas Variables/Watches, Evaluate Expression y breakpoints condicionales. Hay cosas relacionadas que se reservan para secciones/unidades posteriores:

Concepto ¿Por qué no aquí? ¿Dónde se ve?
assert y aserciones Alternativa a excepciones para contratos internos. Sección 11 de UD3.
Testing con JUnit Framework de testing; necesita Maven y dependencias externas. UD2 (intro), UD4 (profundización).
Profiling y análisis de rendimiento Análisis de CPU/memoria; avanzado. Fuera del módulo.
Debugging remoto Depurar programas en otro equipo; avanzado. Fuera del módulo.
Drop Frame (rebobinar la ejecución) Característica avanzada del depurador. Profesional, fuera del módulo.
Breakpoints de método Se detienen al entrar/salir de un método. Necesita métodos propios. UD4.
Breakpoints de excepción Se detienen cuando se lanza una excepción específica. Profesional.
Logging en vez de IO.println Framework de logging (SLF4J, Logback). UD5 / profesional.

10.15 Lo que viene en la sección 11

Con esta sección ya dominas el depurador de IntelliJ. Es una de las herramientas más importantes que aprenderás en todo el ciclo: te va a acompañar en cada programa que escribas a partir de ahora.

En la sección 11 verás assert y aserciones, una herramienta complementaria al depurador: mientras el depurador te permite investigar un bug cuando ocurre, las aserciones te permiten detectar automáticamente si se viola una condición que debería ser cierta. Es como tener un "alarma" que suena si algo inesperado pasa durante la ejecución.

Aprenderás:

  • Sintaxis de assert: assert condicion; y assert condicion : "mensaje";.
  • Cómo activar las aserciones con el flag -ea de la JVM.
  • Diferencia entre assert y excepciones: assert es para contratos internos (cosas que el programador sabe que deben ser ciertas), las excepciones son para errores esperados (cosas que el usuario puede provocar).
  • Precondiciones y postcondiciones de métodos.
  • Cuándo usar assert y cuándo no.

Cuando termines la sección 11, habrás cubierto todo el bloque de detección y corrección de errores de UD3. Después, en la sección 12 verás el test (cuestionarios de repaso) y en la 13 los ejercicios integradores que cierran la unidad.


Cierre del profesor. Llegar hasta aquí es mérito. Acabas de aprender la herramienta que va a cambiar tu forma de programar para siempre. El depurador no es un lujo: es una necesidad. Sin él, estás a ciegas; con él, ves dentro del programa como con rayos X. La regla más importante que te llevas de esta sección es: Step Over (F8) por defecto, Step Into (F7) solo para tus métodos, y breakpoints condicionales para bucles largos. Si interiorizas eso, vas a depurar como un profesional. Nos vemos en la sección 11, donde verás assert, una herramienta más ligera que el depurador para detectar bugs automáticamente mientras el programa corre.