[Java] Lanzamiento de una excepción en el manejo de excepciones

propagación anormal

Cuando un método genera una excepción, si el método actual no detecta la excepción, la excepción se lanzará al método de llamada de nivel superior hasta que encuentre uno que sea try ... catchdetectado:

// exception
public class Main {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            process1();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    static void process1() {
    
    
        process2();
    }

    static void process2() {
    
    
        Integer.parseInt(null); // 会抛出NumberFormatException
    }
}

Al printStackTrace()imprimir la pila de llamadas del método, es similar a:

java.lang.NumberFormatException: null
    at java.base/java.lang.Integer.parseInt(Integer.java:614)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.process2(Main.java:16)
    at Main.process1(Main.java:12)
    at Main.main(Main.java:5)

printStackTrace()Es muy útil para depurar errores. La información anterior indica que se arrojó en el método. De abajo hacia arriba, el nivel de llamada es el siguiente NumberFormatException:java.lang.Integer.parseInt

main()调用process1()process1()调用process2()process2()调用Integer.parseInt(String)Integer.parseInt(String)调用Integer.parseInt(String, int)

Mirando el código fuente de Integer.java, podemos ver que el código del método que arroja una excepción es el siguiente:

public static int parseInt(String s, int radix) throws NumberFormatException {
    
    
    if (s == null) {
    
    
        throw new NumberFormatException("null");
    }
    ...
}

Además, cada llamada de capa proporciona el número de línea del código fuente, que se puede ubicar directamente.

Lanzar excepciones
Cuando ocurre un error, por ejemplo, el usuario ingresa un carácter ilegal, podemos lanzar una excepción.

¿Cómo lanzar una excepción? Consulte Integer.parseInt()el método, lanzando una excepción en dos pasos:

Crea Exceptionuna instancia de a; lanza
con throwdeclaración.
A continuación se muestra un ejemplo:

void process2(String s) {
    
    
    if (s==null) {
    
    
        NullPointerException e = new NullPointerException();
        throw e;
    }
}
实际上,绝大部分抛出异常的代码都会合并写成一行:

void process2(String s) {
    
    
    if (s==null) {
    
    
        throw new NullPointerException();
    }
}

Si un método detecta una excepción y lanza una nueva excepción en la cláusula catch, es equivalente a "convertir" el tipo de excepción lanzada:

void process1(String s) {
    
    
    try {
    
    
        process2();
    } catch (NullPointerException e) {
    
    
        throw new IllegalArgumentException();
    }
}

void process2(String s) {
    
    
    if (s==null) {
    
    
        throw new NullPointerException();
    }
}

Cuando process2()se lanza NullPointerException, process1()se atrapa y luego se lanza IllegalArgumentException().

Si main()está atrapado en IllegalArgumentException, echemos un vistazo a la pila de excepciones impresa:

// exception
public class Main {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            process1();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    static void process1() {
    
    
        try {
    
    
            process2();
        } catch (NullPointerException e) {
    
    
            throw new IllegalArgumentException();
        }
    }

    static void process2() {
    
    
        throw new NullPointerException();
    }
}

La pila de excepciones impresa es similar a:

java.lang.IllegalArgumentException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)

Esto muestra que la nueva excepción ha perdido la información de la excepción original y ya no podemos ver NullPointerExceptionla información de la excepción original.

Para poder rastrear la pila de excepciones completa, al construir una excepción, se pasa la instancia de excepción original y la nueva excepción puede contener la Exceptioninformación original. Las mejoras al código anterior son las siguientes:

// exception
public class Main {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            process1();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    static void process1() {
    
    
        try {
    
    
            process2();
        } catch (NullPointerException e) {
    
    
            throw new IllegalArgumentException(e);
        }
    }

    static void process2() {
    
    
        throw new NullPointerException();
    }
}

Al ejecutar el código anterior, la pila de excepciones impresa es similar a:


java.lang.IllegalArgumentException: java.lang.NullPointerException
    at Main.process1(Main.java:15)
    at Main.main(Main.java:5)
Caused by: java.lang.NullPointerException
    at Main.process2(Main.java:20)
    at Main.process1(Main.java:13)

Tenga en cuenta Caused by: Xxxque lo que se captura IllegalArgumentExceptionno es la causa raíz del problema, la causa raíz es que se incluye NullPointerExceptionen el método.Main.process2()

Para obtener la excepción original en el código, puede usar Throwable.getCause()el método. Si devuelve nulo, significa que ya es una "excepción raíz".

Con la información completa de la pila de excepciones, podemos localizar y solucionar problemas de código rápidamente.

Cuando se detecta una excepción y se vuelve a generar, se debe mantener la excepción original; de lo contrario, ¡será difícil localizar la primera escena del crimen!
Si lanzamos una excepción en un bloque try o catch, ¿se ejecutará la declaración finalmente? Por ejemplo:

// exception
public class Main {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            Integer.parseInt("abc");
        } catch (Exception e) {
    
    
            System.out.println("catched");
            throw new RuntimeException(e);
        } finally {
    
    
            System.out.println("finally");
        }
    }
}

Los resultados de la ejecución del código anterior son los siguientes:

catched
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "abc"
    at Main.main(Main.java:8)
Caused by: java.lang.NumberFormatException: For input string: "abc"
    at ...

Se imprime la primera línea catched, indicando que se ha ingresado el bloque de sentencia catch. Se imprime la segunda línea finally, lo que indica que se ejecuta el bloque de oraciones finally语.

Por lo tanto, catchlanzar una excepción no afectará finallla ejecución de y. JVMse ejecutará primero finallyy luego se lanzará una excepción.

Protección de excepciones
Si se lanza una excepción cuando se ejecuta la declaración finalmente, ¿se puede seguir lanzando la excepción de la declaración catch? Por ejemplo:


// exception
public class Main {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            Integer.parseInt("abc");
        } catch (Exception e) {
    
    
            System.out.println("catched");
            throw new RuntimeException(e);
        } finally {
    
    
            System.out.println("finally");
            throw new IllegalArgumentException();
        }
    }
}

Ejecute el código anterior y encuentre que la información de excepción es la siguiente:

catched
finally
Exception in thread "main" java.lang.IllegalArgumentException
    at Main.main(Main.java:11)

Esto muestra que después de lanzar finalmente una excepción, la excepción que se preparó originalmente para ser lanzada en el catch "desaparece", porque solo se puede lanzar una excepción. Las excepciones que no se lanzan se denominan excepciones "enmascaradas" (Suppressed Exception).

En casos raros, necesitamos saber acerca de todas las excepciones. ¿Cómo guardar toda la información de excepción? El método consiste en guardar primero la excepción original con la variable de origen, luego llamar a Throwable.addSuppressed(), agregar la excepción original y finalmente lanzarla:

// exception
public class Main {
    
    
    public static void main(String[] args) throws Exception {
    
    
        Exception origin = null;
        try {
    
    
            System.out.println(Integer.parseInt("abc"));
        } catch (Exception e) {
    
    
            origin = e;
            throw e;
        } finally {
    
    
            Exception e = new IllegalArgumentException();
            if (origin != null) {
    
    
                e.addSuppressed(origin);
            }
            throw e;
        }
    }
}

Cuando ambos atrapan y finalmente lanzan una excepción, aunque la excepción de captura está protegida, la excepción lanzada por finalmente todavía la incluye:

Exception in thread "main" java.lang.IllegalArgumentException
    at Main.main(Main.java:11)
Suppressed: java.lang.NumberFormatException: For input string: "abc"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.base/java.lang.Integer.parseInt(Integer.java:652)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)
    at Main.main(Main.java:6)

Throwable.getSuppressed()Todo n se puede obtener por Suppressed Exceptio.

En la mayoría de los casos, no introduzca excepciones finalmente. Por lo tanto, generalmente no necesitamos preocuparnos Suppressed Exception.

Al hacer preguntas, publique excepciones.
La información detallada de la pila impresa por las excepciones es la clave para descubrir el problema. Muchos principiantes solo publican códigos cuando hacen preguntas, pero no publican excepciones.

¿Quién te enseñó a no publicar montones de excepciones al hacer preguntas?

Algunos zapatos para niños solo publican alguna información anormal, y se omiten las más importantes.Esta Caused by: xxxes una forma incorrecta de hacer preguntas y debe cambiarse.

resumen

La llamada printStackTrace()puede imprimir la pila de propagación de excepciones, que es muy útil para la depuración;

Al capturar una excepción y lanzar una nueva excepción nuevamente, se debe mantener la información de la excepción original;

Normalmente no finallylanza excepciones en . Si finalmente se lanza una excepción, la excepción original debe agregarse a la excepción original. La persona que llama puede Throwable.getSuppressed()obtener todo agregado por Suppressed Exception.

Supongo que te gusta

Origin blog.csdn.net/ihero/article/details/132165769
Recomendado
Clasificación