Vienen excepciones, se capturan excepciones, se lanzan excepciones, excepciones personalizadas

1. Atrapa la excepción:

        En Java, cualquier instrucción que pueda generar una excepción puede capturarse con try...catch. Coloque las declaraciones que pueden causar excepciones en try{...}, y luego use catch para capturar la Excepción correspondiente y sus subclases.

        1. Múltiples declaraciones de captura

                Se pueden utilizar varias instrucciones catch, y cada captura captura la excepción correspondiente y sus subclases. Después de que la JVM detecta una excepción, hace coincidir la instrucción catch de arriba a abajo.Después de hacer coincidir una captura determinada, ejecuta el bloque de código catch y luego no continúa con la coincidencia. En pocas palabras, solo se puede ejecutar una de las múltiples instrucciones catch. P.ej:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println(e);
    } catch (NumberFormatException e) {
        System.out.println(e);
    }
}

                Cuando hay capturas múltiples, el orden de las capturas es muy importante y la subclase debe escribirse primero, por ejemplo:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println("IO error");
    } catch (UnsupportedEncodingException e) { // 永远捕获不到
        System.out.println("Bad encoding");
    }
}

                Para el código anterior, UnsupportedEncodingException nunca puede capturarse porque es una subclase de IOException. Cuando se lanza UnsupportedEncodingException, será capturado y ejecutado por catch(IOException e){...}. Por lo tanto, el enfoque correcto debería ser poner el campo de la subclase primero:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    }
}

       

        2. finalmente declaración 

                Ya sea que haya o no una excepción, todos queremos ejecutar algunas declaraciones, como el trabajo de limpieza, podemos escribir la declaración de ejecución varias veces: poner la ejecución normal en try y escribirla nuevamente para cada captura, por ejemplo:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
        System.out.println("END");
    } catch (IOException e) {
        System.out.println("IO error");
        System.out.println("END");
    }
}

                Pero esta línea de código es System.out.println("¡¡China ganará la lucha contra la epidemia!!!"); pero si se lanza el método de llamada anterior y ocurre una excepción, esta línea de código no se ejecutará, entonces podemos usar el bloque finalmente para asegurarnos de que se ejecutará con o sin errores, y el código anterior se puede reescribir como:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    } finally {
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    }
}

                Preste atención a varias características de finalmente:

                        1. La declaración final no es necesaria, puede ser escrita o no;

                        2.finally siempre se ejecuta en último lugar;

               Si no ocurre ninguna excepción, el bloque try{...} se ejecuta normalmente y finalmente se ejecuta. Si ocurre una excepción, se interrumpe la ejecución del bloque try{...}, luego se salta la ejecución del bloque catch coincidente y finalmente se ejecuta. Se puede ver que finalmente se usa para asegurar que se debe ejecutar algún código.

                En algunos casos, también podemos usar la estructura try...finally sin catch, por ejemplo:

void process(String file) throws IOException {
    try {
        ...
    } finally {
        System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的
    }
}

        3. Captura una variedad de excepciones:

                Si la lógica de procesamiento de algunas excepciones es la misma, pero no existe una relación de herencia antes de la excepción en sí, debe escribir varias instrucciones catch, por ejemplo:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException e) {
        System.out.println("Bad input");
    } catch (NumberFormatException e) {
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

                Debido a que el código para manejar IOException y NumberFormatException es el mismo, podemos combinar su uso dual | juntos, por ejemplo:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException | NumberFormatException e) { // IOException或NumberFormatException
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

                Use try...catch...finally para capturar el resumen de la excepción:

                        1. El orden de coincidencia de varias sentencias catch es muy importante, y las subclases deben colocarse primero.

                        2. La declaración finalmente asegura que se ejecutará con o sin excepción, y es opcional.

                        3. Una instrucción catch también puede coincidir con varias excepciones no hereditarias.

2. Lanza una excepción:

        1. Ruta de propagación anormal:

                Cuando un método arroja 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 se detecte un intento... captura:

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
    }
}

                La pila de llamadas del método se puede imprimir a través de printStackTrace(), por ejemplo:

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 significa: NumberFormatException se lanza en el método java.lang.Integer.parseInt. De abajo hacia arriba, la capa de llamada es:

                        1.main() llama a proceso1();

                        2.process1() llama a process2();

                        3.process2() llama a Integer.parseInt(String);

                        4.Integer.parseInt(String)调用Integer.parseInt(String,int);

                Mirando el código fuente de Integer.java, podemos ver que el código del método de lanzamiento de excepciones es el siguiente:

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

        2. Lanzar una excepción

                Cuando ocurre un error, como que el usuario ingrese un carácter ilegal, podemos lanzar una excepción.¿Cómo lanzar una excepción? Haciendo referencia al método Integer.parseInt() anterior, lanzar una excepción se divide en dos pasos:

                        1. Cree una instancia de una Excepción;

                        2. Use la sentencia throw para lanzar;

                P.ej:

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

                De hecho, la mayoría del código que genera una excepción se combinará en una línea como la comentada anteriormente o la siguiente:

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

                Si un método detecta una excepción y luego lanza una nueva excepción en la misma captura, es equivalente a convertir el tipo de la 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() lanza NullPointerException, es capturado por process1() y luego lanza IllegalArgumentException(). Si IllegalArgumentException se detecta en main(), Wine imprimirá la pila de excepciones para ver, de la siguiente manera:

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 se ve así:

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 o la excepción fundamental, y ya no podemos ver la información de la excepción original NullPointerException. Para rastrear la información completa de la pila de excepciones, al construir una excepción, pase la instancia de excepción original y la nueva excepción puede contener la información de excepción original. Modifique el código anterior de la siguiente manera:

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 la siguiente:

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)

                Nota : Causado por: Xxx, lo que indica que la IllegalArgumentException capturada no es la causa raíz del problema, la causa raíz es NullPointerException, que se lanzó en el método Main.process2(). Para obtener la información de excepción sin procesar en el código, puede usar el método Throwable.getCause(). Si devuelve nulo, significa que la excepción es la raíz que causó el problema. Con la información completa de la pila de excepciones, podemos ubicar y modificar rápidamente el código para resolver el problema.

Al capturar una excepción y lanzarla nuevamente, asegúrese de mantener la excepción original para que la primera escena del crimen se pueda encontrar más rápido. ! !

                Si lanzamos una excepción en un bloque try o catch, ¿seguirá ejecutándose el bloque finalmente? P.ej:

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");
        }
    }
}

                El resultado de la ejecución del código anterior es el siguiente:

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 ...

                La primera línea imprime catchd, indicando que se ingresó el bloque de instrucción catch, y la segunda línea imprime finalmente, indicando que se ejecuta el bloque de instrucción finalmente, por lo que podemos saber que cuando se lanza una excepción en el catch, no afectará el finalmente ejecución, JVM se ejecutará finalmente primero y luego lanzará una excepción.

        3. Protección de excepción:

                Si se lanza una excepción cuando se ejecuta la instrucción finalmente, ¿puede continuar el lanzamiento del bloque catch? P.ej:

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 la información de excepción de la siguiente manera:

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

                La salida de la consola muestra que después de lanzar finalmente una excepción, la excepción que se iba a lanzar en la captura "desaparece" porque solo se puede lanzar una excepción. Una excepción que no se produce se denomina excepción "enmascarada" (Excepción suprimida). En casos raros, necesitamos conocer todas las excepciones, entonces, ¿cómo guardar toda la información de excepción? El método es primero guardar el campo original en una variable, luego llamar a Throwable.addSuppressed(), agregar el campo original y luego agregarlo finalmente, de la siguiente manera:

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 capturan y finalmente lanzan excepciones, aunque la excepción de captura está enmascarada, la excepción finalmente lanzada todavía la contiene:

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)

                Todas las excepciones suprimidas se pueden obtener a través de Throwable.getSuppressed(). En la gran mayoría de los casos, no introduzca excepciones finalmente. Por lo general, no nos preocupamos por la excepción suprimida.

                

               Resumen de excepción de lanzamiento:

                        1. Llamar a printStackTrace() puede imprimir la pila de propagación de excepciones, lo cual es muy efectivo para depurar programas;

                        2. Cuando se detecta una excepción y se lanza una nueva excepción, se debe conservar la información de la excepción original;

                        3. Por lo general, no incluya excepciones finalmente. Si finalmente se lanza una excepción, la excepción original debe agregarse a la excepción original, y la persona que llama puede obtener todas las excepciones suprimidas agregadas a través de Throwable.getSuppressed().

3. Excepción personalizada:

                Cuando necesitemos lanzar excepciones en nuestro código, intente usar las excepciones ya definidas por el JDK. Por ejemplo: el tipo de parámetro no es válido, deberíamos lanzar una IllegalArgumentException:

static void process1(int age) {
    if (age <= 0) {
        throw new IllegalArgumentException();
    }
}

                Por supuesto, en proyectos grandes, se pueden personalizar nuevos tipos de excepciones, pero es muy importante mantener un sistema de herencia razonable. La práctica habitual es personalizar una excepción como una "excepción raíz" y luego derivar varios servicios. . La excepción raíz debe derivarse de una excepción adecuada; por lo general, se recomienda derivar de RuntimeException:

public class BaseException extends RuntimeException {
}

                Todos los demás tipos de excepción se pueden derivar de la excepción raíz:

public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}

...

                Las excepciones raíz personalizadas deben proporcionar múltiples constructores (preferiblemente correspondientes a RuntimeException):

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}

                Consulte la implementación nativa de RuntimeException en el constructor anterior. De esta forma, cuando se lanza una excepción, se puede seleccionar el constructor apropiado.

                Resumen de excepción personalizado:

                        1. Cuando se lanza una excepción, reutilice el tipo de excepción ya definido por el JDK tanto como sea posible;

                        2. Al personalizar el sistema de excepciones, se recomienda derivar la excepción raíz de la clase RuntimeException y luego derivar otros tipos de excepciones de la excepción raíz.

                        3. Al personalizar las excepciones, intente imitar la clase RuntimeException y proporcione varios métodos de construcción.

Supongo que te gusta

Origin blog.csdn.net/chen_kai_fa/article/details/123614980
Recomendado
Clasificación