programacion - ¿Por qué la gente de Java con frecuencia consume excepciones en silencio?
manual de programacion android pdf (28)
- 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.
- Muchos programadores son perezosos (incluido yo), y es muy fácil imprimir el seguimiento de la pila.
Nunca hice una codificación Java seria antes, pero aprendí la sintaxis, 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);
}
}
Como otros han señalado, la razón por la que ve esto es por una de estas tres razones:
- 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 .
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.
Debería ver esto muy a menudo si el programador 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).
El verdadero objetivo 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 programadores 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 programadores 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:
- The error is handled right away in context not suitable to decide what to do with it.
- It is displayed or ignored silently and computing continues even when further code has no chance to run properly.
- The caller of method can not differentiate whether it finished successfully or not.
How to write the "right way" than?
- Indicate every base class of exceptions which can be thrown in method header. AFAICR Eclipse can do it automatically.
- Make the throw list in method prototype meaningful. Long lists are pointless and "throw Exception" is lazy (but useful when you not bother much about exceptions).
- When writing the "wrong way" simple "throw Exception" is much better and takes less bytes than "try{ ... } catch(Exception e) { e.printStackTrace(); }".
- Rethrow chained exception if needed.
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 pila 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.
Encuentro que a menudo hay 2 razones por las que esto se hace
- El programador era flojo
- El programador 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 codificación 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.
Es una práctica perezosa, nada menos que eso.
Normalmente se hace cuando realmente no te importa la excepción, en lugar de aumentar el trabajo con los dedos.
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".
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.
Me temo que la mayoría de los programadores de Java no saben qué hacer con Excepciones, y siempre lo consideran como una molestia que ralentiza su codificación 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 programador de este tipo (sucede con frecuencia) le doy dos entradas de lectura:
- el famoso Thinking In java
- Un breve e interesante artículo de Barry Ruzek disponible aquí: www.oracle.com/technology/pub/articles/dev2arch/2006/11/effective-exceptions.html
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.
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 pila 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 programació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.
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.
Porque aún no aprendieron este truco:
class ExceptionUtils {
public static RuntimeException cloak(Throwable t) {
return ExceptionUtils.<RuntimeException>castAndRethrow(t);
}
@SuppressWarnings("unchecked")
private static <X extends Throwable> 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);
}
}
}
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 declaración 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 :)
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:
- Ignorancia.
- +
- 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
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''.
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 programadores de Java. Claro, los programadores de Java pueden ser perezosos, como cualquier otro programador, 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 declaración 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 programadores de Java con respecto al manejo de errores; Conozco a muchos de tus amados programadores 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.
porque Checked Exceptions es un experimento fallido
(tal vez printStackTrace () es el verdadero problema ? :)
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.
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".
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.
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.
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).
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.
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.