¿Por qué la gente de Java con frecuencia consume excepciones en silencio?

Nunca hice una encoding Java seria antes, pero aprendí la syntax, las bibliotecas y los conceptos basados ​​en mis habilidades existentes (Delphi & C #). Una cosa que apenas entiendo es que he visto tanto código que silenciosamente consume excepciones después de printStackTrace así:

  public void process() { try { System.out.println("test"); } catch(Exception e) { e.printStackTrace(); } } 

Hay un código similar a este en casi todos los artículos y proyectos de Java con los que me encontré. Según mi conocimiento, esto es muy malo. La excepción casi siempre se debe reenviar al contexto externo de esta manera:

  public void process() { try { System.out.println("test"); } catch(Exception e) { e.printStackTrace(); throw new AssertionError(e); } } 

La mayoría de las veces la excepción debe ser manejada en el bucle más externo que pertenece al marco subyacente (Java Swing, por ejemplo). ¿Por qué parece la norma codificar así en el mundo de Java? Estoy confundido.

Según mis antecedentes, preferiría eliminar printStackTrace por completo . Simplemente volvería a lanzar como una aka RuntimeException no manejada (o, mejor aún, AssertionError ), y luego atraparla y registrarla en el lugar más apropiado: el bucle externo del framework.

  public void process() { try { System.out.println("test"); } catch(Exception e) { throw new AssertionError(e); } } 

Siempre he pensado que es similar a la siguiente situación:

“Un hombre recibe un disparo.

Él contiene la respiración y tiene la fuerza suficiente para tomar un autobús.

10 millas después, el hombre baja del autobús, camina un par de cuadras y muere “.

Cuando la policía llega al cuerpo, no tienen ni idea de lo que acaba de suceder. Pueden tener eventualmente, pero es mucho más difícil.

Mejor es:

“Un hombre recibe un disparo y muere instantáneamente, y el cuerpo yace exactamente donde acaba de ocurrir el asesinato”.

Cuando llega la policía, toda la evidencia está en su lugar.

Si un sistema falla, mejor es fallar rápidamente

Dirigiéndose a la pregunta:

  1. Ignorancia.
      +
  2. Perezoso

EDITAR:

Por supuesto, la sección de captura es útil.

Si se puede hacer algo con la excepción, ahí es donde debería hacerse.

Probablemente eso NO sea una excepción para el código dado, probablemente es algo que se espera (y en mi analogía es como una chaqueta a prueba de balas, y el hombre estaba esperando el disparo en primer lugar).

Y sí, la captura podría usarse para Lanzar excepciones apropiadas a la abstracción

Por lo general, esto se debe a que el IDE ofrece una “solución rápida” útil que envuelve el código ofensivo en un bloque try-catch con ese manejo de excepciones. La idea es que realmente HAGA algo, pero los desarrolladores perezosos no lo hacen.

Esto es mala forma, sin dudas.

  1. Java le obliga a manejar todas las excepciones de forma explícita. Si se declara que un método que llama su código arroja FooException y BarException su código DEBE manejar (o lanzar) esas excepciones. La única excepción a esto es RuntimeException , que es silenciosa como un ninja.
  2. Muchos progtwigdores son perezosos (incluido yo), y es muy fácil imprimir el seguimiento de la stack.

Este es un argumento clásico de hombre paja . printStackTrace() es una ayuda de depuración. Si lo vio en un blog o en una revista fue porque el escritor estaba más interesado en ilustrar un punto que no sea ​​el manejo de excepciones. Si lo vio en el código de producción, el desarrollador de ese código era ignorante o flojo, nada más. No debe ser considerado como un ejemplo de práctica común en el “mundo de Java”.

Encuentro que a menudo hay 2 razones por las que esto se hace

  1. El progtwigdor era flojo
  2. El progtwigdor quería proteger un punto de entrada en el componente allí (correcta o incorrectamente)

No creo que este sea un fenómeno limitado a Java. También he visto esa encoding en C # y VB.Net.

En la superficie es bastante impactante y se ve terrible. Pero realmente no es nada nuevo. Ocurre todo el tiempo en aplicaciones C ++ que usan valores de retorno de código de error frente a excepciones. La diferencia, sin embargo, es que ignorar un valor de retorno potencialmente fatal no parece realmente diferente de llamar a una función que devuelve vacío.

 Foo* pFoo = ...; pFoo->SomeMethod(); // Void or swallowing errors, who knows? 

Este código se ve mejor, pero si SomeMethod () dijera return a HResult, sería semánticamente no diferente de tragar una excepción.

porque Checked Exceptions es un experimento fallido

(tal vez printStackTrace () es el verdadero problema ? 🙂

Tengo que decir que me molesta un poco el tono que implica este tipo de comportamiento de manejo de errores laxos es algo fundamental para los progtwigdores de Java. Claro, los progtwigdores de Java pueden ser perezosos, como cualquier otro progtwigdor, y Java es un lenguaje popular, por lo que probablemente veas muchas excepciones para descifrar el código.

Además, como se ha señalado en otro lugar, existen frustraciones comprensibles con la statement forzada de excepciones verificadas de Java, aunque personalmente no tengo ningún problema con eso.

Lo que tengo un problema, supongo, es que estás revisando un montón de artículos y fragmentos de código en la web sin molestarte en considerar el contexto. La verdad es que cuando estás escribiendo un artículo técnico tratando de explicar cómo funciona una determinada API, o cómo empezar con algo, entonces es muy probable que te saltes algunos aspectos del código, el manejo de errores que no está directamente relacionado. relacionado con lo que está demostrando es un posible candidato para su eliminación, especialmente si no es probable que ocurra la excepción en el escenario de ejemplo.

Las personas que escriben artículos de esa naturaleza tienen que mantener una relación señal / ruido razonable, y bastante justa, creo, eso significa que deben suponer que conocen algunos conceptos básicos sobre el idioma en el que están desarrollando; cómo lidiar adecuadamente con los errores y un montón de otras cosas. Si te encuentras con un artículo y notas la falta de una correcta comprobación de errores, está bien; solo asegúrate de que cuando incorpores esas ideas (pero, por supuesto, nunca el código exacto, ¿verdad?) en tu código de producción, lidiarás con todos esos bits y bobs que el autor dejó de lado, de una manera que es más adaptado a lo que estás desarrollando.

Tengo un problema con los artículos introductorios de muy alto nivel que revisan estos temas sin volver a ellos, pero tenga en cuenta que no existe una “mentalidad” particular de los progtwigdores de Java con respecto al manejo de errores; Conozco a muchos de tus amados progtwigdores de C # que tampoco se molestan en lidiar con todos sus problemas.

Una impresión System.out o e.printStackTrace () – que implica el uso de System.out suele ser una bandera roja que significa que alguien no se molestó en hacer un trabajo diligente. Exceptuando las aplicaciones de escritorio de Java, la mayoría de las aplicaciones de Java están mejor utilizando el registro.

Si el modo de falla de un método es una no operación, está perfectamente bien comerse una excepción, ya sea que registre el motivo (y la existencia) o no. Más típicamente, sin embargo, la cláusula catch debería estar tomando algún tipo de acción excepcional.

Retirar una excepción es algo que se hace mejor cuando usa el catch para limpiar parte del trabajo en un nivel donde la información necesaria todavía está disponible o cuando necesita transformar la excepción en un tipo de excepción más receptiva para la persona que llama.

Solo se consume silenciosamente si el bloque catch está realmente vacío.

En cuanto a los artículos, probablemente sean más interesantes para probar algún otro punto además de cómo tratar con las excepciones. Solo quieren ir directo al grano y tener el código más corto posible.

Obviamente tienes razón, las excepciones deberían al menos registrarse si van a ser ‘ignoradas’.

Como otros han señalado, la razón por la que ve esto es por una de estas tres razones:

  1. Un IDE generó el bloque try-catch
    • El código fue copiado y pegado
    • El desarrollador puso la stacktrace para depurar pero nunca volvió a manejar la excepción correctamente

El último punto es el menos probable de ocurrir. Digo esto porque no creo que nadie realmente depure de esta manera. Recorrer el código con un depurador es una forma mucho más fácil de depurar.

La mejor descripción de lo que debería hacerse en un bloque catch se puede encontrar en el Capítulo 9 de Java efectivo de Joshua Bloch .

En C #, todas las excepciones son excepciones de tiempo de ejecución, pero en Java tiene excepciones de tiempo de ejecución y excepciones comprobadas, que debe capturar o declarar en sus métodos. Si llama a un método que tiene un “lanzamiento” al final, debe capturar las excepciones mencionadas allí, o su método también debe declarar esas excepciones.

Los artículos de Java generalmente solo imprimen el seguimiento de la stack o tienen un comentario porque el manejo de la excepción es irrelevante para el tema del artículo. Sin embargo, en los proyectos, se debe hacer algo al respecto, dependiendo del tipo de excepción.

Debería ver esto muy a menudo si el progtwigdor hace bien su trabajo. ¡Ignorar la excepción es una mala, mala práctica! Pero hay algunas razones por las que algunos pueden hacer esto y las soluciones más adecuadas:

  • “¡Esto no sucederá!” Claro, en algún momento “sabes” que esta Excepción no sucederá, pero es aún más apropiado volver a lanzar una excepción de tiempo de ejecución con la Excepción que se ha ocultado como “causa” en lugar de ignorarla. Apuesto a que ocurrirá en algún momento en el futuro. 😉

  • Código de creación de prototipos Si solo está escribiendo sus datos para ver si funcionan, es posible que desee ignorar todas las excepciones que puedan aparecer. Este es el único caso en el que hago una captura floja (Throwable). Pero si el código se convertirá en algo útil, incluyo el manejo de excepciones adecuado.

  • “¡No se que hacer!” Vi mucho código, especialmente código de biblioteca, que se traga excepciones que ocurren porque en esta capa de la aplicación no se puede hacer un manejo adecuado. NO HAGAS ESTO! Simplemente vuelva a lanzar la excepción (agregando una cláusula throws en la firma del método o envolviendo la excepción en una biblioteca específica).

Siempre debe reenviarlo o manejarlo adecuadamente en un contexto del mundo real. Sin embargo, muchos artículos y tutoriales simplificarán su código para obtener los puntos más importantes y una de las cosas más fáciles de simplificar es la gestión de errores (a menos que lo que está haciendo sea escribir un artículo sobre el manejo de errores :)). Como el código java verificará el manejo de excepciones, poner un bloque de captura silencioso (o statement de explotación) simple es el método más simple para proporcionar un ejemplo de trabajo.

Si encuentra esto en algo que no sea un código de ejemplo, siéntase libre de reenviar el código a TDWTF, aunque pueden tener demasiados ejemplos de esto por ahora 🙂

Me temo que la mayoría de los progtwigdores de Java no saben qué hacer con Excepciones, y siempre lo consideran como una molestia que ralentiza su encoding del caso “nominal”. Por supuesto que están totalmente equivocados, pero es difícil convencerlos de que ES IMPORTANTE lidiar correctamente con las excepciones. Cada vez que me encuentro con un progtwigdor de este tipo (sucede con frecuencia) le doy dos entradas de lectura:

Por cierto, estoy totalmente de acuerdo en que es estúpido capturar una excepción mecanografiada para volver a lanzarla incrustada en una RuntimeException:

  • si lo agarras, MANEJALO.
  • de lo contrario, cambie la firma de su método para agregar posibles excepciones que usted / no podría manejar, para que la persona que llama tenga la oportunidad de hacerlo por su cuenta.

La combinación de excepciones comprobadas e interfaces conduce a la situación de que el código debe manejar las excepciones que nunca se lanzan. (Lo mismo se aplica a la herencia normal, también, pero es más común y más fácil de explicar con interfaces)

Motivo: la implementación de una interfaz no puede arrojar excepciones (comprobadas) distintas de las definidas en la especificación de la interfaz. Por esa razón, los creadores de una interfaz, sin saber qué métodos de una clase que implementa la interfaz realmente podrían necesitar lanzar una excepción, podrían especificar que todos los métodos arrojen al menos un tipo de excepción. Ejemplo: JDBC, donde se declara que todo y su abuela lanzan SQLException.

Pero en realidad, muchos métodos de implementaciones reales simplemente no pueden fallar, por lo que bajo ninguna circunstancia arrojan una excepción. El código que llama a este método debe de alguna manera “manejar” la excepción, y la forma más fácil es hacer que trague la excepción. Nadie quiere desordenar su código con un manejo de errores aparentemente inútil que nunca se ejecuta.

Como se señaló, llamar a printStackTrace () no es realmente un manejo silencioso.

La razón de este tipo de “deglución” de la excepción es que, si continúa pasando la excepción por la cadena, aún tiene que manejar la excepción en algún lugar o dejar que la aplicación falle. Por lo tanto, manejarlo al nivel que ocurre con un volcado de información no es peor que manejarlo en el nivel superior con un volcado de información.

Es una práctica perezosa, nada menos que eso.

Normalmente se hace cuando realmente no te importa la excepción, en lugar de boost el trabajo con los dedos.

No estoy de acuerdo con que volver a lanzar una excepción marcada sea una mejor idea. Captura significa manejo; si tienes que volver a tirar, no deberías atrapar. Agregaría la cláusula throws a la firma del método en ese caso.

Diría que es aceptable ajustar una excepción marcada en una no marcada (por ejemplo, la forma en que Spring ajusta la SQLException comprobada en una instancia de su jerarquía sin marcar).

El registro se puede considerar manejo. Si el ejemplo se cambió para registrar el seguimiento de la stack usando log4j en lugar de escribir en la consola, ¿eso lo haría aceptable? No mucho cambio, OMI.

El verdadero problema es lo que se considera excepcional y un procedimiento de recuperación aceptable. Si no puede recuperarse de la excepción, lo mejor que puede hacer es informar el error.

Por favor, nunca, nunca, nunca envuelva una excepción marcada en una excepción sin marcar.

Si te encuentras lidiando con excepciones que no crees que debas, entonces mi consejo es que probablemente estés trabajando en el nivel incorrecto de abstracción.

Elaboraré: las excepciones controladas y sin marcar son dos bestias muy diferentes. Las excepciones comprobadas son similares al antiguo método de devolver códigos de error … sí, son algo más difíciles de manejar que los códigos de error, pero también tienen sus ventajas. Las excepciones no verificadas son errores de progtwigción y fallas críticas del sistema … excepciones excepcionales en otras palabras. Al tratar de explicar qué excepción hay, muchas personas se meten en un lío completo porque no reconocen la diferencia en estos dos casos, muy diferentes.

Porque aún no aprendieron este truco:

 class ExceptionUtils { public static RuntimeException cloak(Throwable t) { return ExceptionUtils.castAndRethrow(t); } @SuppressWarnings("unchecked") private static  X castAndRethrow(Throwable t) throws X { throw (X) t; } } class Main { public static void main(String[] args) { // Note no "throws" declaration try { // Do stuff that can throw IOException } catch (IOException ex) { // Pretend to throw RuntimeException, but really rethrowing the IOException throw ExceptionUtils.cloak(ex); } } } 

El verdadero objective de las excepciones es simplificar el manejo de errores y separarlo de la detección de errores. Esto es contrario a la representación de errores por códigos de error, donde el código de manejo de errores está disperso en todas partes y cada llamada que puede fallar debe ser revisada por código de retorno.

Si la excepción representa un error (que es la mayoría de los casos), generalmente la forma más razonable de manejarlo es rescatar y dejar el manejo en alguna capa superior. Debería considerarse la posibilidad de volver a tirar diferentes excepciones si se le agrega alguna semántica significativa, es decir, este error es un problema inusual del sistema / temporal (de red) / esto es un error del cliente o del servidor, etc.

De todas las estrategias de manejo de errores, el más ignorante se esconde o simplemente imprime un mensaje de error y avanza como si nada hubiera pasado.


Los usuarios de Sun querían que el código fuera más explícito y obligaban a los progtwigdores a escribir qué excepciones podían arrojarse con cada método. Parecía un movimiento correcto: cualquiera sabrá qué esperar a cambio de cualquier llamada a un método dado su prototipo (puede devolver un valor de este tipo o lanzar una instancia de una de las clases especificadas (o su subclase)).

Pero como resultó con muchos progtwigdores Java ignorantes, ahora tratan el manejo de excepciones como si fuera un error de lenguaje / “característica” que necesitaba una solución y escribir el código de la peor o la peor manera posible:

  • El error se maneja de inmediato en un contexto no adecuado para decidir qué hacer con él.
  • Se muestra o se ignora silenciosamente y la informática continúa incluso cuando el código adicional no tiene posibilidad de ejecutarse correctamente.
  • La persona que llama del método no puede diferenciar si terminó exitosamente o no.

¿Cómo escribir el “camino correcto” que?

  • Indique cada clase base de excepciones que pueden arrojarse en el encabezado del método. AFAICR Eclipse puede hacerlo automáticamente.
  • Haga que la lista de lanzamientos en el prototipo del método sea significativa. Las listas largas no tienen sentido y “throw Exception” es flojo (pero útil cuando no te molestas demasiado con las excepciones).
  • Al escribir el “camino equivocado”, simple “throw Exception” es mucho mejor y toma menos bytes que “try {…} catch (Exception e) {e.printStackTrace ();}”.
  • Retira la excepción encadenada si es necesario.

If you want your exception to be handled outside the scope of the current method you don’t need to to catch it actually, instead you add ‘throws’ statement to the method signature.

The try/catch statements that you’ve seen are only in the code where programmer explicitly decided to handle the exception in place and therefore not to throw it further.

I think developers also try to consider the importance of “doing the right thing” in a particular context. Often times for throw away code or when propagating the exception upwards wouldn’t buy anything because the exception is fatal, you might as well save time and effort by “doing the wrong thing”.

You would usually swallow an exception when you cannot recover from it but it is not critical. One good example is the IOExcetion that can be thrown when closing a database connection. Do you really want to crash if this happens? That’s why Jakarta’s DBUtils have closeSilently methods.

Now for checked exception that you cannot recover but are critical (usually due to programming errors), don’t swallow them. I think exceptions should be logged the nearest as possible to the source of the problem so I would not recommend removing the printStackTrace() call. You will want to turn them into RuntimeException for the sole purpose of not having to declare these exceptions in you business method. Really it doesn’t make sense to have high level business methods such as createClientAccount() throws ProgrammingErrorException (read SQLException), there is nothing you can do if you have typos in your sql or accessing bad indexes.

From experience, Swallowing an exception is harmful mostly when it’s not printed. It helps to bring attention if you crash, and I’ll do that deliberately at times, but simply printing the exception and continuing allows you to find the problem and fix it, and yet usually doesn’t negatively effect others working on the same codebase.

I’m actually into Fail-Fast/Fail-HARD, but at least print it out. If you actually “Eat” an exception (which is to truly do nothing: {}) It will cost someone DAYS to find it.

The problem is that Java forces you to catch a lot of stuff that the developer knows won’t be thrown, or doesn’t care if they are. The most common is Thread.sleep(). I realize there are holes that might allow threading issues here, but generally you know that you are not interrupting it. Período.

There can be many reasons why one would use catch Exception. In many cases it is a bad idea because you also catch RuntimeExceptions – and well you don’t know in what state the underlying objects will be after this happens ? That is always the difficult thing with unexpected conditions: can you trust that the rest of the code will not fail afterwards.

Your example prints the stacktrace so at least your will know what the root cause might have been. In bigger software projects it is a better idea to log these things. And lets hope that the log component does not throw exceptions either our you might end up in an infinite loop (which will probably kill your JVM).

If you have a checked exception and you don’t want to handle it in a method, you should just have the method throw the exception. Only catch and handle exceptions if you are going to do something useful with it. Just logging it is not very useful in my book as users rarely have time to be reading logs looking for exceptions or know what to do if an exception is thrown.

While wrapping the exception is an option, I would not suggest you do this unless; you are throwing a different exception to match an exist interface or there really is no way such an exception should be thrown.

BTW: If you want to re throw a checked exception you can do this with

 try { // do something } catch (Throwable e) { // do something with the exception Thread.currentThread().stop(e); // doesn't actually stop the current thread, but throws the exception/error/throwable } 

Note: if you do this, you should make sure the throws declaration for the method is correct as the compiler is unable to do this for you in this situation.

because it is a best practice. I thought everybody knew.
but the cold truth is that nobody really understands how to work with exceptions. The C error handing style made so much more sense.