Saltar a contenido

Tratamiento de excepciones

Un programa que trate las excepciones debe realizar los siguientes pasos:

  1. Se intenta (try) ejecutar un bloque de código.
  2. Si se produce una circunstancia excepcional se lanza (throw) una excepción. En caso contrario el programa sigue su curso normal.
  3. Si se ha lanzado una excepción, la ejecución del programa es desviada al manejador de excepciones donde la excepción se captura (catch) y se decide qué hacer al respecto.
try{
    //Instrucciones que se intentan ejecutar, si se produce una
    //situación inesperada se lanza una excepción
} catch(tipoExcepcion e){
    //Instrucciones para tratar esta excepción
} catch(otroTipoExcepcion e){
    //Instrucciones para tratar esta excepción
}
//Se pueden escribir tantos bloques catch como sean necesarios
finally{
    //instrucciones que se ejecutarán siempre después de un bloque try,
    //se haya producido o no una excepción
}

Bloque try

  • En el bloque try se encuentran las instrucciones que pueden lanzar una excepción.
  • Solamente se pueden capturar las excepciones lanzadas dentro del bloque try.
  • Una excepción se puede lanzar de forma automática o mediante la palabra reservada throw.
  • Cuando se lanza la excepción se transfiere la ejecución del programa desde el punto donde se lanza la excepción a otro punto donde se captura.

Bloque catch

  • Es el bloque de código donde se captura la excepción.
  • El bloque catch es el manejador o handler de la excepción.
  • Aquí se decide qué hacer con la excepción capturada.
  • Puede haber varios bloques catch relacionados con un bloque try.
  • Una vez finalizado un bloque catch la ejecución no vuelve al punto donde se lanzó la excepción, sino que continúa por la primera instrucción a continuación de los bloques catch.

Bloque finally

  • Es un bloque opcional y debe aparecer a continuación de los bloques catch.
  • También puede aparecer a continuación de un bloque try si no hay bloques catch.
  • La ejecución de sus instrucciones queda garantizada independientemente de que el bloque try acabe o no su ejecución, incluso en los siguientes casos:

    • Aunque el bloque try tenga una sentencia return, continue o break, se ejecutará el bloque finally.
    • Cuando se haya lanzado una excepción que ha sido capturada por un bloque catch. El finally se ejecuta después del catch correspondiente.
    • Si se ha lanzado una excepción que no ha sido capturada, se ejecuta finally antes de acabar el programa.
  • Un bloque finally se usa para dejar un estado consistente después de ejecutar el bloque try.

  • Un ejemplo de uso de bloques finally puede ser cuando estamos tratando con ficheros y se produce una excepción. Podemos escribir un bloque finally para cerrar el fichero. Este bloque se ejecutará siempre y se liberarán los recursos ocupados por el fichero.
/*El siguiente programa lee un número entero y lo muestra. Si en la instrucción sc.nextInt() 
se introduce un número de otro tipo o un carácter, se lanza una excepción InputMismatchException 
que es capturada por el bloque catch. 
En este bloque se realizan las instrucciones necesarias para 
resolver la situación y que el programa pueda continuar.*/

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int n;
    do{
        try{
            System.out.print("Introduce un número entero > 0: ");
            n = sc.nextInt();
            System.out.println("Num. introducido: " + n);
        } catch(InputMismatchException e){
            sc.nextLine(); //retorna la tecla enter
            n = 0;
            System.out.println("Introduzca un entero " + e.toString());
        }
    }while(n<=0);
}

Captura de excepciones

Un bloque try puede estar seguido de varios bloques catch, tantos como excepciones diferentes queramos manejar.

La estructura y el comportamiento de un bloque catch son similares al de un método. La excepción es capturada por el bloque catch cuyo argumento coincida con el tipo de objeto lanzado.

La búsqueda de coincidencia se realiza sucesivamente sobre los bloques catch en el orden en que aparecen en el código hasta que aparece la primera concordancia.

Cuando acaba la ejecución de este bloque, el programa continúa después del último de los catch que sigan al bloque try que lanzó la excepción.

Cuidado con las anidaciones

El sistema de control de excepciones puede ser anidado a cualquier nivel. Debe mantenerse la regla de que un bloque try debe ser seguido por un catch. Pueden existir secuencias try-catch dentro de bloques try y de bloques catch.

Cada catch debe tener como parámetro un objeto de la clase Throwable, de una clase derivada de ella o de una clase definida por el programador.

Cuando se lanza una excepción se captura por el primer bloque catch cuyo parámetro sea de la misma clase que el objeto excepción o de una clase base directa o indirecta. Por este motivo, es importante el orden en que se coloquen los bloques catch.

Las excepciones más genéricas se deben capturar al final. Si no es necesario tratar excepciones concretas de forma específica se puede poner un bloque catch de una clase base que las capture todas y las trate de forma general. Esto se conoce como captura genérica de excepciones.

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    int [] array = {4,2,6,7};
    int n;
    boolean repetir = false;
    do{
        try{
            repetir = false;
            System.out.print("Un entero > 0 y < " + array.length + " ");
            n = sc.nextInt();
            System.out.println("Valor en la posición " + n + ": " + array[n]);
        } catch(InputMismatchException e){
            sc.nextLine();
            n = 0;
            System.out.println("Debe introducir un entero ");
            repetir = true;
        } catch(IndexOutOfBoundsException e){
            System.out.println("Un entero > 0 y < " + array.length + " ");
            repetir = true;
        } catch(Exception e){
            // resto de excepciones de tipo Exception
            // y derivadas
            System.out.println("Error inesperado " + e.toString());
            repetir = true;
        }
    }while(repetir);
}

Actividades

  • AC 802 (RA3 / CE3d CE3f CE3i / IC1 / 3p). Escriba un programa que permita al usuario jugar a adivinar un número. El ordenador debe generar un número entero entre 1 y 100 y el usuario intentará adivinarlo. Para ello, cada vez que el usuario introduce un valor, el ordenador debe indicar si el número buscado es mayor o menor que el que se ha introducido. Cuando consiga adivinarlo debe indicarlo e imprimir en pantalla el número de intentos necesarios para encontrar el número. Si el usuario no introduce un número entero debe lanzarse y capturarse una excepción, mostrando la forma correcta de hacerlo, y contarlo como un intento.

  • AC 803 (RA3 / CE3d CE3f CE3i / IC1 / 3p). Dado el siguiente programa en Java trata las excepciones que se puedan producir.

    import java.util.Scanner;
    
    public class Ejercicio3 {
    
        static Scanner sc = new Scanner(System.in);
    
        public static void main(String[] args) {
            double n;
            int posicion;
            String cadena;
            double[] valores = {9.83, 4.5, -3.06, 0.06, 2.52, -11.3, 7.60, 3.00, -30.4, 105.2};
    
            System.out.println("Contenido del array antes de modificar:");
            for (int i = 0; i < valores.length; i++) {
                System.out.printf("%.2f ", valores[i]);
            }
    
            System.out.print("\n\nIntroduce la posición del array a modificar: ");
            cadena = sc.nextLine();
            posicion = Integer.parseInt(cadena);
    
            System.out.print("\nIntroduce el nuevo valor de la posición " + posicion + ": ");
            n = sc.nextDouble();
    
            valores[posicion] = n;
    
            System.out.println("\nPosición a modificar: " + posicion);
            System.out.println("Nuevo valor: " + n);
            System.out.println("Contenido del array modificado:");
            for (int i = 0; i < valores.length; i++) {
                System.out.printf("%.2f ", valores[i]);
            }
        }
    }
    
  • AC 804 (RA3 / CE3d CE3f CE3i / IC1 / 3p). El siguiente programa permite al lectura por entrada estándar de 2 números enteros y 2 decimales. Modifícalo para evitar que haya duplicidad en el código.

    import java.util.*;
    
    public class Ejercicio4 {
    
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int x, y;
            double m, n;
            boolean repetir;
    
            do {
                repetir = false;
                try {
                    System.out.print("Introduce número entero: ");
                    x = sc.nextInt();
                } catch (InputMismatchException e) {
                    System.out.println("Valor no válido " + e.toString());
                    sc.nextLine();
                    repetir = true;
                }
            } while (repetir);
    
            do {
                repetir = false;
                try {
                    System.out.print("Introduce número entero: ");
                    y = sc.nextInt();
                } catch (InputMismatchException e) {
                    System.out.println("Valor no válido " + e.toString());
                    sc.nextLine();
                    repetir = true;
                }
            } while (repetir);
    
            do {
                repetir = false;
                try {
                    System.out.print("Introduce número decimal: ");
                    m = sc.nextDouble();
                } catch (InputMismatchException e) {
                    System.out.println("Valor no válido " + e.toString());
                    sc.nextLine();
                    repetir = true;
                }
            } while (repetir);
    
            do {
                repetir = false;
                try {
                    System.out.print("Introduce número decimal: ");
                    n = sc.nextDouble();
                } catch (InputMismatchException e) {
                    System.out.println("Valor no válido " + e.toString());
                    sc.nextLine();
                    repetir = true;
                }
            } while (repetir);
    
            System.out.println("int introducido -> " + x);
            System.out.println("int introducido -> " + y);
            System.out.println("double introducido -> " + m);
            System.out.println("double introducido -> " + n);
        }
    }
    
  • AC 805 (RA3 / CE3d CE3f CE3i / IC1 / 3p). El siguiente programa simplemente asigna un archivo .jpg a un objeto imagen de la clase BufferedImage.

    • a. Indica qué sucede al compilarlo. Explicación.
    • b. Modifica el código para solucionar esa situación con una excepción genérica.
    • c. Realiza una nueva modificación para que el programa capture exactamente la excepción lanzada
    import java.awt.image.BufferedImage;
    import javax.imageio.ImageIO;
    import java.io.File;
    
    public class Ejerc3 {
        public static void main(String[] args) {
            // Asignación de un archivo .jpg al objeto imagen
            BufferedImage imagen = ImageIO.read(new File("src/lugar/casa.jpg"));
        }
    }
    
  • AC 806 (RA3 / CE3d CE3f CE3i / IC1 / 3p). Compila la clase Cable siguiente y comprueba el error que te aparece:

    package cable;
    
    public class Cable {
        public static void main(String[] args) {
            Cable c1 = new Cable();
            c1.conectar();
        }
    
        void conectar() {
            Cable c2 = null; 
            c2.mostrar();
        }
    
        void mostrar() {
            System.out.println("Conexión establecida");
        }
    }
    
    • a. Indica cuál es el significado del error de ese mensaje de error.
    • b. Corrige el código para que funcione correctamente.