2010-12-29 14 views
43

Estoy trabajando en la limpieza de algunos de mis códigos y llegué a un punto en el que no estaba seguro de qué ruta sería mejor.Múltiple o simple Try Catch

Actualmente tengo un único bloque de captura de prueba sobre la mayoría de mi método y maneja algunas excepciones por separado al final, pero pensé que sería mejor tener más bloques de prueba para el mantenimiento. Sin embargo, mientras descifraba el código llegué a un punto en el que estaba escribiendo varios bloques para el mismo tipo de excepción. Puedo ver el lado positivo de escribir un bloque para cada parte ya que puedo dar más detalles de por qué falló.

Mi pregunta es esta ... ¿hay algún inconveniente en hacer esto? ¿Podría haber problemas de rendimiento o algún otro monstruo oculto que no estoy viendo?

Además, ¿cuál es la forma preferida de manejar múltiples excepciones en un método? ¿Existe un estándar de la industria?

Sólo para ilustrar mejor mi punto aquí es un código de pseudo

//multiple try catch for same exception 
try { 
    //some code here 
} catch (MyException e) { 
    //specific error message here 
} 
try { 
    //some different code here 
} catch (MyException e) { 
    //more specific error message indicating a different issue 
} 
+1

Parece que necesita escribir un mensaje de error por cada excepción lanzada, cerca del punto donde se lanza. ¿Pero por qué? A menudo, la única información importante es que la operación como un todo falló. – Raedwald

+1

@Raedwald: Siento que agregar más información sobre por qué falló la operación será útil para el mantenimiento de las personas que trabajan con esto después de mí. Estoy usando algunos complementos de código abierto así que para cualquiera que no los conozca puedo agregar un poco más de detalle. – Shaded

+3

Es por eso que el registro es tan importante, sombreado. No tiene que cargar su código con un procesamiento de error específico si, como sugiere Raedwalk, la recuperación es inútil de todos modos. Mi técnica general es registrar todo lo que considero importante y volver a lanzar la excepción. De hecho, si el error no tiene remedio, vuelvo a lanzar una excepción de tiempo de ejecución y la capturo en el nivel más alto para la limpieza final. –

Respuesta

46

Siempre intento para reducir los niveles de anidamiento para la legibilidad y facilidad de mantenimiento. Si n tienen los bloques try/catch, cada uno manejando el mismo tipo de excepción, por qué no refactorizar el código que puede lanzar la excepción de métodos ... que sería algo como:

try { 
    firstBatchOfTricky(); 
    secondBatchOfTricky(); 
    .... 
    nthBatchOfTricky(); 
} catch (ItWentBoomException e) { 
    // recover from boom 
} catch (ItWentBangException e) { 
    // recover from bang 
} 

Esto es mucho más fácil de leer que tener múltiples intentos/capturas. Tenga en cuenta que sus métodos deben describir lo que hacen en el espíritu del código de autoedición.

Dado que tiene su propio tipo de excepción, puede agregar los datos que necesita a la excepción para hacer cosas diferentes en el bloque catch. Cuando dice 'mensaje más específico', puede simplemente lanzar la excepción con el mensaje detallado; no deberías necesitar múltiples bloques catch. Si desea hacer cosas drásticamente diferentes en función del estado de la excepción, simplemente cree más tipos de excepción y atrape bloques, pero solo un bloque try, ya que mi pseudocódigo muestra ...

Finalmente, si no puede recuperar desde la (s) excepción (es), no debe saturar el código con bloques de captura. Lanza una excepción de tiempo de ejecución y deja que burbujee. (Un buen consejo de @tony en los comentarios)

+0

Muy buen punto, creo que en realidad trataré de refactorizar (como ha sugerido cualquiera que haya respondido) y de hecho dividir parte de este lío en una clase diferente. – Shaded

+0

Tenga cuidado al refacturar para no cambiar el comportamiento de su código. Por ejemplo, si antes tenía dos bloques try/catch, digamos A y B, y el código en A se manejó sin reiniciar, entonces se ejecutaría el código en el B. Si simplemente combina dos bloques en uno, este comportamiento cambiará si A arroja una excepción, B ya no se ejecutará. ¡Diviértete refactorizando! – rodion

+0

Tengo una pregunta en mi mente, ¿qué pasa si atrapamos una excepción generalizada i-e Exception en un bloque catch junto con estas excepciones? ... ¿el código se ejecutará correctamente? o todas las excepciones irán a ese bloque catch que capturará la excepción de nivel superior "Exception"? o habrá algún tipo de excepción de tiempo de compilación/tiempo de ejecución? –

4

Si las excepciones se pueden manejar de manera diferente (mensaje de error diferente, etc.), es correcto capturarlas por separado.

Si los tipos de excepción son diferentes, eso no significa que tenga que tener bloques try por separado, puede tener un bloque try con varias capturas.

+0

Sí, pero estaba filmando para obtener más, ¿y si en un bloque de prueba única tengo dos líneas que arrojan el mismo tipo de excepción? – Shaded

+0

Si los maneja de manera diferente, entonces está bien tener dos bloques catch. Pero plantea preguntas que muchas de las otras respuestas persiguen. – jzd

+0

Estoy completamente de acuerdo, gracias por tomar un ángulo diferente en la respuesta: D – Shaded

1

Mi preferencia personal es una sola prueba que puede tener o no múltiples bloques catch. Mis métodos tienden a ser lo suficientemente pequeños donde esto tiene sentido. Si el suyo se está haciendo demasiado extenso para que sea práctico, tal vez deba pensar en refactorizar.

Una cosa sobre catch blocks: si tu catch block imprime o registra la pila y vuelve a lanzar, diría que sería mejor agregar la excepción a la cláusula throws en la firma del método y dejarla burbujear arriba.

0

Excelente pregunta. Voy a decir que deberías usar un solo bloque try-catch. Mi razonamiento: si tiene motivos para dividir su método en varios bloques try-catch, probablemente debería pensar en refactorizar su método en múltiples métodos, cada uno con su propio bloque try-catch.

0

Intento evitar repetir el código siempre que sea posible.Entonces, si la respuesta a un cierto tipo de excepción es la misma, tómalo una vez.

0

Prefiero el solo bloque try-catch. Si la longitud del método es muy grande, entonces prefiero dividir el método en múltiples métodos internos para la modularidad del alumno.
También a veces es posible que pueda volver a utilizar estos métodos internos.
Me siento de esta manera es mucho más limpio y mejor para el mantenimiento.

0

Depende de la lógica de su código - puede interrumpir su proceso (cálculo o lo que sea) en cualquier momento o puede/debe/necesita/debe controlar el flujo y revertir/notificar/verificar/atribuir si se indica alguna condición de error excepción en un paso particular.

Si está implementando un cliente RSS simple, puede mantener todos los códigos relacionados con el socket en un bloque. Pero si está escribiendo un cliente de bittorrent, necesitará una máquina de estado y un manejo de excepciones mucho más granulado.

1

Estoy de acuerdo con el consenso general de las respuestas anteriores, que un solo bloque try-catch es mejor. Sin embargo, para cuando tu método sea lo suficientemente largo como para hacerte mirar esta pregunta, es probable que sea demasiado largo. Y puede ser más fácil extraer nuevos métodos si el manejo de excepciones es más local. Aún así, creo que su mejor opción puede ser extraer métodos que simplemente arrojen la excepción y dejen el manejo de excepciones en su lugar, en el método original, idealmente en un solo bloque try-catch.

38

Esta no es una pregunta sobre desempeño o preferencias personales: es una pregunta de funcionalidad y requisitos.

Supongamos que escribo:

Escenario 1:

try 
{ 
    doThingA(); 
} 
catch (SomeException panic) 
{ 
    System.out.println("doThingA failed"); 
} 
try 
{ 
    doThingB(); 
} 
catch (SomeException panic) 
{ 
    System.out.println("doThingB failed"); 
} 

Escenario 2:

try 
{ 
    doThingA(); 
    doThingB(); 
} 
catch (SomeException panic) 
{ 
    System.out.println("doThingA or doThingB failed"); 
} 

Estos dos escenarios no son equivalentes: Ellos hacen cosas diferentes. En el Escenario 1, si doThingA arroja la excepción, doThingB todavía se ejecuta. En el Escenario 2, si doThingA arroja la excepción, doThingB no se ejecuta. Entonces, la pregunta no es cuál da un mejor rendimiento o cuál es un código más legible, sino que, si doThingA falla, ¿debería doThingB seguir ejecutándose o no?

Si lo que realmente quiere es la segunda comportamiento, pero desea diferentes mensajes para indicar al usuario lo que salió mal, entonces debe o bien lanzar excepciones diferentes, o poner el texto del mensaje en la excepción, es decir,

void doThingA() throws SomeException 
{ 
    ... whatever code ... 
    if (theWorldIsAboutToEnd) 
    throw new SomeException("doThingA failed"); 
} 

Luego, en la cláusula catch, en lugar de mostrar una cadena constante, se muestran SomeException.toString o SomeException.getMessage.

+0

Muy buen punto, sin embargo, en mi situación actual, cualquier excepción detiene la operación y la arroja a un nivel superior para seguir funcionando. – Shaded

+0

Entonces no puedes tener dos capturas pequeñas. No funcionará Si el primer bloque try encuentra una excepción, el segundo bloque try aún se ejecutará. – Jay

+3

perfecto, de hecho, estaba buscando exactamente estos dos escenarios que se abordarán – khurramengr

0

Parece una pregunta muy antigua. Pero mi respuesta lo relataría porque en los últimos días Java 7 ha agregado una nueva función para agregar múltiples exacepciones y el bloque catch. Esto eliminaría la gran cantidad de código y se ve muy interesante. Me encuentro con este link explianing sobre el concepto try-with-resource.

1

Es totalmente dependiente del escenario. Si hace un montón de cosas que arroja una excepción de seguridad y maneja ese escenario de la misma manera, puede usar un bloque de prueba para eso.

Sin embargo, una vez dicho esto, usar muchos bloques de prueba/captura más pequeños es a menudo mejor en mi opinión.Usar un try/catch grande es como usar varias declaraciones goto, excepto que no se puede decir de dónde vienen los gotos.

Por ejemplo, ¿cómo se puede saber qué método iría a la excepción1 y cuál iría a la excepción 2? Está bien si es un bloque pequeño, pero una vez que se hace más grande, se vuelve muy ilegible.

try{ 
    doThing1(); 
    doThing2(); 
    doThing3(); 
    doThing4(); 
    doThing5(); 
    doThing6(); 
    doThing7(); 
    doThing8(); 
    doThing9(); 
} catch (Exception1 e){ 

} catch (Exception2 e){ 

} 

A continuación está más claro.

try{ 
    doThing1();  
} catch (Exception1 e){ [...] } 
    doThing2(); 
    doThing3(); 
    doThing4(); 
    doThing5(); 
    doThing6(); 
    doThing7(); 
    doThing8(); 
try{ 
    doThing9(); 
} catch (Exception2 e){ [...] } 
0

Para estar seguro siempre utilizar bloques intento de captura individuales para cada declaración que lanza una excepción ya que si se escribe todo en un solo bloque try luego, si cualquier declaración se produce una excepción a continuación, no se ejecutan las instrucciones escritas debajo de ella.