Java Language Atrapando una excepción con try-catch


Ejemplo

Se puede capturar y manejar una excepción utilizando la declaración try...catch . (De hecho, las declaraciones de try toman otras formas, como se describe en otros ejemplos sobre try...catch...finally y try-with-resources ).

Prueba-captura con un bloque de captura

La forma más simple se ve así:

try {
    doSomething();
} catch (SomeException e) {
    handle(e);
}
// next statement

El comportamiento de un simple try...catch es el siguiente:

  • Se ejecutan las sentencias en el bloque try .
  • Si las declaraciones en el bloque try no generan ninguna excepción, entonces el control pasa a la siguiente instrucción después del try...catch .
  • Si se lanza una excepción dentro del bloque try .
    • El objeto de excepción se prueba para ver si es una instancia de SomeException o un subtipo.
    • Si es así, entonces la catch bloque detectar la excepción:
      • La variable e está vinculada al objeto de excepción.
      • Se ejecuta el código dentro del bloque catch .
      • Si ese código lanza una excepción, entonces la excepción recién lanzada se propaga en lugar de la original.
      • De lo contrario, el control pasa a la siguiente instrucción después del try...catch .
    • Si no lo es, la excepción original continúa propagándose.

Prueba-captura con múltiples capturas

Un try...catch también puede tener varios bloques de catch . Por ejemplo:

try {
    doSomething();
} catch (SomeException e) {
    handleOneWay(e)
} catch (SomeOtherException e) {
    handleAnotherWay(e);
}
// next statement

Si hay varios bloques de catch , se intentan uno por uno comenzando con el primero, hasta que se encuentra una coincidencia para la excepción. El controlador correspondiente se ejecuta (como anteriormente) y luego el control se pasa a la siguiente instrucción después de la instrucción try...catch . Los bloques de catch posteriores a los que coinciden siempre se omiten, incluso si el código del manejador lanza una excepción .

La estrategia de coincidencia "de arriba abajo" tiene consecuencias para los casos en que las excepciones en los bloques catch no son desunidas. Por ejemplo:

try {
    throw new RuntimeException("test");
} catch (Exception e) {
    System.out.println("Exception");
} catch (RuntimeException e) {
    System.out.println("RuntimeException");
}

Este fragmento de código generará "Exception" en lugar de "RuntimeException". Dado que RuntimeException es un subtipo de Exception , la primera catch (más general) coincidirá. La segunda catch (más específica) nunca será ejecutada.

La lección para aprender de esto es que los bloques de catch más específicos (en términos de los tipos de excepción) deben aparecer primero, y los más generales deben ser los últimos. (Algunos compiladores de Java le avisarán si una catch nunca puede ejecutarse, pero esto no es un error de compilación).

Bloques de captura multi-excepción

Java SE 7

A partir de Java SE 7, un solo bloque catch puede manejar una lista de excepciones no relacionadas. El tipo de excepción se enumera, separado por un símbolo de barra vertical ( | ). Por ejemplo:

try {
    doSomething();
} catch (SomeException | SomeOtherException e) {
    handleSomeException(e);
} 

El comportamiento de una captura de múltiples excepciones es una extensión simple para el caso de excepción única. La catch coincide si la excepción lanzada coincide (al menos) con una de las excepciones enumeradas.

Hay alguna sutileza adicional en la especificación. El tipo de e es una unión sintética de los tipos de excepción en la lista. Cuando se utiliza el valor de e , su tipo estático es el supertipo menos común de la unión de tipos. Sin embargo, si e es rethrown dentro del bloque catch , los tipos de excepción que se lanzan son los tipos en la unión. Por ejemplo:

public void method() throws IOException, SQLException
    try {
        doSomething();
    } catch (IOException | SQLException e) {
        report(e);
        throw e;
    }

En lo anterior, IOException y SQLException son excepciones revisadas cuyo supertipo menos común es Exception . Esto significa que el método de report debe coincidir con el report(Exception) . Sin embargo, el compilador sabe que el throw puede lanzar solamente una IOException o un SQLException . Por lo tanto, el method se puede declarar como throws IOException, SQLException lugar de throws Exception . (Lo que es bueno: vea Pitfall - Lanzar Throwable, Exception, Error o RuntimeException ).