Excepción lanzada dentro del bloque de captura: ¿se capturará nuevamente?

180

Esto puede parecer una pregunta de programación 101 y pensé que sabía la respuesta, pero ahora necesito verificarlo dos veces. En el siguiente código, ¿la excepción lanzada en el primer bloque de captura será capturada por el bloque de captura de excepción general a continuación?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

Siempre pensé que la respuesta sería no, pero ahora tengo un comportamiento extraño que podría ser causado por esto. La respuesta es probablemente la misma para la mayoría de los idiomas, pero estoy trabajando en Java.

roryf
fuente
2
¿Quizás podría describir el "comportamiento extraño"?
Jeffrey L Whitledge
¿Estás seguro de que ApplicationException no se está lanzando a otro lado y se está propagando hasta este bloque?
sblundy
Me di cuenta de esto en mi Eclipse IDE. Es agotador forzarme a poner el "lanzar nueva excepción" en un bloque de prueba, pero no sé por qué. Lo he hecho en el pasado sin hacer eso. No veo por qué se requeriría un bloque de prueba. Muchos ejemplos en Google muestran que las personas no necesitan un bloque de prueba. ¿Es porque estoy lanzando dentro de una declaración if?
djangofan

Respuestas:

214

No, ya que lo nuevo throwno está en el trybloque directamente.

Chris Jester-Young
fuente
Digamos que si el código es así, intente {} catch (Exception e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("En catch IOException:" + e.getClass ()); } y el código en el bloque try genera IO Exception, ¿irá al bloque de excepción general inmediata o volará al bloque catch de IOException?
sofs1
44
@ user3705478 El código no se compilará para evitar ese tipo de situación errónea. En general, no se le permite tener una captura para una subclase después de una captura de su superclase en el mismo bloque de prueba.
Chris Jester-Young
Gracias. Entonces obtendré un error de tiempo de compilación, ¿verdad? Lo probaré cuando llegue a casa.
sofs1
Depende de @ user3705478, si RuntimeExceptionse arroja un catchbloque, no habrá error de compilación.
Randy el Dev.
@ AndrewDunn No creo que de eso se trate la pregunta de user3705478, sino de qué sucede si una catchcláusula de excepción principal aparece antes que una catchcláusula de excepción secundaria . Tengo entendido que Java no permite esto, y está atrapado en el momento de la compilación.
Chris Jester-Young
71

No. Es muy fácil de verificar.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

Debería imprimir:

En captura IOException: clase java.io.IOException
En finalmente
Excepción en el hilo "main" java.lang.RuntimeException
        en Catch.main (Catch.java:8)

Técnicamente, podría haber sido un error del compilador, un comportamiento no especificado dependiente de la implementación o algo así. Sin embargo, el JLS está bastante bien definido y los compiladores son lo suficientemente buenos para este tipo de cosas simples (el caso de los genéricos puede ser un asunto diferente).

También tenga en cuenta que si intercambia los dos bloques catch, no se compilará. La segunda captura sería completamente inalcanzable.

Tenga en cuenta que el bloque finalmente siempre se ejecuta incluso si se ejecuta un bloque catch (que no sean casos tontos, como bucles infinitos, adjuntando a través de la interfaz de herramientas y matando el hilo, reescribiendo bytecode, etc.).

Tom Hawtin - tackline
fuente
3
La forma más obvia de evitar un finallyes, por supuesto, llamar System.exit. :-P
Chris Jester-Young
3
@Chris Jester-Young for(;;);es más corto, contenido en el lenguaje, no introduce muchos efectos secundarios y, para mí, más obvio.
Tom Hawtin - tackline
1
System.exites más amigable con la CPU! : -O Pero sí, está bien, claramente este es un criterio subjetivo. Además, no sabía que eras un jugador de código. ;-)
Chris Jester-Young
28

La especificación del lenguaje Java dice en la sección 14.19.1:

Si la ejecución del bloque try se completa abruptamente debido a un lanzamiento de un valor V, entonces hay una opción:

  • Si el tipo de tiempo de ejecución de V es asignable al parámetro de cualquier cláusula catch de la declaración try, entonces se selecciona la primera cláusula catch (más a la izquierda). El valor V se asigna al parámetro de la cláusula catch seleccionada y se ejecuta el Bloque de esa cláusula catch. Si ese bloque se completa normalmente, entonces la instrucción try se completa normalmente; si ese bloque se completa abruptamente por alguna razón, entonces la instrucción try se completa abruptamente por la misma razón.

Referencia: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

En otras palabras, la primera captura adjunta que puede manejar la excepción sí lo hace, y si se arroja una excepción de esa captura, eso no está dentro del alcance de ninguna otra captura para el intento original, por lo que no intentarán manejarla.

Una cosa relacionada y confusa de saber es que en una estructura try- [catch] -finally, un bloque finalmente puede lanzar una excepción y si es así, se pierde cualquier excepción lanzada por el bloque try o catch. Eso puede ser confuso la primera vez que lo ves.

Alex Miller
fuente
Solo quería agregar que desde Java 7 puede evitar eso usando try-with-resources. Luego, si tryY finallyambos lanzan, finallyse suprime, pero también se AGREGA a excepción de try. Si también catchlanza, no tiene suerte, a menos que lo maneje usted mismo a través de 'addSuppressed' y agregue la tryexcepción, entonces tiene los tres.
LAFK dice Reinstate Monica
6

Si desea lanzar una excepción desde el bloque catch, debe informar su método / clase / etc. que necesita lanzar dicha excepción. Al igual que:

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

Y ahora tu compilador no te gritará :)

Mastergeek
fuente
4

No, como dijo Chris Jester-Young, se lanzará al próximo try-catch en la jerarquía.

Ian P
fuente
2

Como se dijo anteriormente ...
Agregaría que si tiene problemas para ver lo que está sucediendo, si no puede reproducir el problema en el depurador, puede agregar un rastro antes de volver a lanzar la nueva excepción (con el buen sistema anterior .out.println en el peor de los casos, con un buen sistema de registro como log4j).

PhiLho
fuente
2

No será atrapado por el segundo bloque de captura. Cada excepción se captura solo cuando está dentro de un bloque de prueba. Sin embargo, puede anidar intentos (no es una buena idea en general):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}
Vinko Vrsalovic
fuente
Escribí un código similar. Pero no estoy convencido. Quiero llamar a un Thread.sleep () en mi bloque catch. Pero Thread.sleep se lanza una excepción interrumpida. ¿Es correcto (una práctica recomendada) hacerlo como ha mostrado en su ejemplo?
riroo
1

No, dado que todas las capturas se refieren al mismo bloque try, por lo que lanzar desde dentro de un bloque catch sería atrapado por un bloque try cerrado (probablemente en el método que llamó a este)

Uri
fuente
-4

La publicación anterior pero la variable "e" debe ser única:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}
Ted K
fuente
55
Esto debería ser un comentario y no una respuesta. No proporciona nada en la forma de abordar la pregunta real: es solo una crítica de la pregunta de los OP.
Derek W