Nunca antes hice una codificación seria de Java, 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
esto:
public void process() {
try {
System.out.println("test");
} catch(Exception e) {
e.printStackTrace();
}
}
Hay un código similar como 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 debería reenviarse al contexto externo como este:
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 debería terminar manejándose 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 Java? Estoy confundido.
Según mis antecedentes, preferiría eliminar printStackTrace por completo . Simplemente lo volvería a lanzar como un aka no controlado RuntimeException
(o, mejor aún, AssertionError
), luego lo capturaría y lo registraría en el lugar más apropiado: el bucle más externo del marco.
public void process() {
try {
System.out.println("test");
} catch(Exception e) {
throw new AssertionError(e);
}
}
fuente
Respuestas:
Siempre he pensado que es similar al siguiente escenario:
Cuando la policía llega al cuerpo, no tienen ni idea de lo que acaba de suceder. Puede que eventualmente lo hayan hecho, pero es mucho más difícil.
Mejor es:
Cuando llega la policía, todas las pruebas están en su lugar.
Si un sistema va a fallar, mejor es fallar rápido
Abordar la pregunta:
+
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 sea algo que se espera (y en mi analogía es como una chaqueta antibalas, y el hombre estaba esperando el disparo en primer lugar).
Y sí, la captura podría usarse para lanzar excepciones apropiadas para la abstracción
fuente
Por lo general, eso 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 HAGAS algo, pero los desarrolladores perezosos no lo hacen.
Esto es de mala forma, sin duda.
fuente
Este es un argumento clásico del hombre de 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 un ignorante o un vago, nada más. No debe considerarse un ejemplo de práctica común en el "mundo java".fuente
fuente
Encuentro que a menudo hay 2 razones por las que esto se hace
No creo que este sea un fenómeno limitado a Java. También he visto este tipo de codificación a menudo 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 utilizan valores de retorno de código de error frente a excepciones. Sin embargo, la diferencia es que ignorar un valor de retorno potencialmente fatal no se ve realmente diferente a 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 que devuelve un HResult, semánticamente no sería diferente a tragarse una excepción.
fuente
porque las Excepciones marcadas son un experimento fallido
(¿quizás printStackTrace () es el verdadero problema ? :)
fuente
Debo decir que me molesta un poco el tono que implica que este tipo de comportamiento laxo en el manejo de errores es algo fundamental para los programadores de Java. Claro, los programadores de Java pueden ser vagos, como cualquier otro programador, y Java es un lenguaje popular, por lo que probablemente verá una gran cantidad de excepciones al tragar código.
Además, como se ha señalado en otro lugar, hay frustraciones comprensibles con la declaración forzada de Java de excepciones comprobadas, aunque personalmente no tengo ningún problema con eso.
Con 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á escribiendo un artículo técnico tratando de explicar cómo funciona una API en particular, o cómo comenzar con algo, es muy probable que se salte algunos aspectos del código: el manejo de errores que no es directamente relacionado con lo que está demostrando es un candidato probable para su eliminación, especialmente si es poco 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 creo que de manera bastante justa eso significa que deben asumir que conoces algunos conceptos básicos sobre el lenguaje en el que te estás desarrollando; cómo tratar correctamente los errores y un montón de otras cosas. Si encuentra un artículo y nota una falta de verificación de errores adecuada, entonces está bien; solo asegúrese de que cuando incorpore esas ideas (pero, por supuesto, nunca el código exacto en sí, ¿verdad?) en su código de producción, se ocupará de todos esos bits y bobs que el autor omitió sensatamente, de una manera que sea más adecuado a lo que está desarrollando.
Tengo un problema con los artículos introductorios de muy alto nivel que tratan 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 sus queridos programadores de C # que tampoco se molestan en lidiar con todos sus problemas.
fuente
try {...} catch (IOException e) {}
es simplemente estúpido (y un TODO NO ayuda). Seguramente engañará a muchos principiantes para que hagan lo mismo. Además, es mucho más escribir quethrows IOException
, lo cual es correcto la mayor parte del tiempo. Quizás una vez por trabajo, no sea posible y luego escribirthrow wrap(e)
(sin aburrir a definirwrap
) es una buena solución. Una de las primeras cosas que me apoyé en Java es que tragar en silencio es algo terrible y tengo mucho cuidado para no hacerlo ni siquiera temporalmente (incluso fallar es mucho mejor a largo plazo).Una impresión de System.out o e.printStackTrace (), que implica el uso de System.out, suele ser una señal de alerta, lo que significa que alguien no se molestó en hacer un trabajo diligente. A excepción de las aplicaciones Java de escritorio, la mayoría de las aplicaciones Java están mejor utilizando el registro.
Si el modo de falla de un método es una no operación, está perfectamente bien comer una excepción, ya sea que registre el motivo (y la existencia) o no. Sin embargo, lo más común es que la cláusula de captura debería tomar algún tipo de acción excepcional.
Relanzar una excepción es algo que se hace mejor cuando usa la captura 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 accesible para la persona que llama.
fuente
Solo se consume silenciosamente si el bloque de captura está realmente vacío.
En lo que respecta a los artículos, probablemente sean más interesantes para demostrar algún otro punto además de cómo tratar las excepciones. Solo quieren ir directo al grano y tener el código más corto posible.
Sin embargo, obviamente tiene razón, las excepciones deberían al menos registrarse si van a ser 'ignoradas'.
fuente
Como han señalado otros, la razón por la que ve esto es por una de tres razones:
El último punto es el que tiene menos probabilidades de ocurrir. Digo esto porque no creo que nadie realmente depure de esta manera. Pasar por el código con un depurador es una forma mucho más sencilla de depurar.
La mejor descripción de lo que se debe hacer en un bloque de captura se puede encontrar en el Capítulo 9 de Effective Java de Joshua Bloch .
fuente
En C # todas las excepciones son excepciones en tiempo de ejecución, pero en Java tiene excepciones en tiempo de ejecución y excepciones comprobadas, que debe detectar o declarar en sus métodos. Si llama a cualquier método que tenga "throws" 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 excepciones es irrelevante para el tema del artículo. Sin embargo, en los proyectos, se debe hacer algo al respecto, según el tipo de excepción.
fuente
Debería ver esto muy a menudo si el programador hace bien su trabajo. ¡Ignorar la excepción es una mala práctica! Pero hay algunas razones por las que algunos podrían hacer esto y las soluciones más adecuadas:
"¡Esto no sucederá!" Seguro, en algún momento "sabes" que esta excepción no sucederá, pero es aún más apropiado volver a generar una excepción en tiempo de ejecución con la excepción que ocurrió como "causa" en lugar de simplemente ignorarla. Apuesto a que ocurrirá en el futuro. ;-)
Código de creación de prototipos Si solo está escribiendo sus cosas para ver si funciona, es posible que desee ignorar todas las excepciones que puedan ocurrir. Este es el único caso en el que hago algunas capturas perezosas (arrojables). Pero si el código se convierte en algo útil, incluyo el manejo adecuado de excepciones.
"¡No se que hacer!" Vi mucho código, especialmente código de biblioteca, que se traga las excepciones que ocurren porque en esta capa de la aplicación no se puede realizar un manejo adecuado. ¡NO HAGA ESTO! Simplemente vuelva a generar la excepción (ya sea agregando una cláusula throws en la firma del método o envolviendo la excepción en una biblioteca específica).
fuente
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 transmitir los puntos más importantes y una de las cosas más fáciles de simplificar es el manejo de errores (a menos que lo que esté haciendo sea escribir un artículo sobre el manejo de errores, es decir :)). Como el código java verificará el manejo de excepciones, poner un bloque de captura silencioso simple (o declaración de registro) 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, no dude en reenviar el código a TDWTF, aunque es posible que ya tengan demasiados ejemplos :)
fuente
Me temo que la mayoría de los programadores de Java no saben qué hacer con las Excepciones, y siempre lo consideran como una molestia que ralentiza la codificación del caso "nominal". Por supuesto que están totalmente equivocados, pero es difícil convencerlos de que ES importante tratar correctamente las excepciones. Cada vez que me encuentro con un programador 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 escrita para volver a lanzarla incrustada en una RuntimeException:
fuente
La combinación de excepciones e interfaces comprobadas conduce a la situación de que el código debe manejar ejecuciones 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)
Razón: La implementación de una interfaz no puede generar excepciones (marcadas) 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 podrían necesitar lanzar una excepción, podrían especificar que todos los métodos podrían lanzar al menos un tipo de excepción. Ejemplo: JDBC, donde todo y su abuela se declara para lanzar SQLException.
Pero en realidad, muchos métodos de implementaciones reales simplemente no pueden fallar, por lo que bajo ninguna circunstancia lanzan una excepción. El código que llama a estos métodos todavía debe "manejar" la excepción de alguna manera, y la forma más fácil es tragar la excepción. Nadie quiere saturar su código con un manejo de errores aparentemente inútil que nunca se ejecuta.
fuente
Como se señaló, llamar a printStackTrace () no es un manejo realmente silencioso.
La razón de este tipo de "tragar" la excepción es que, si sigue pasando la excepción en la cadena, todavía tiene que manejar la excepción en alguna parte o dejar que la aplicación se bloquee. Entonces, manejarlo en el nivel en que ocurre con un volcado de información no es peor que manejarlo en el nivel superior con un volcado de información.
fuente
Es una práctica perezosa, nada menos que en realidad.
Por lo general, se hace cuando realmente no le importa la excepción, en lugar de aumentar su trabajo con los dedos.
fuente
No estoy de acuerdo en que volver a lanzar una excepción marcada sea una mejor idea. La captura significa manipulación; si tiene que volver a lanzar, no debe atrapar. En ese caso, agregaría la cláusula throws a la firma del método.
Yo diría que envolver una excepción marcada en una no marcada (por ejemplo, la forma en que Spring envuelve la SQLException marcada en una instancia de su jerarquía sin marcar) es aceptable.
La tala se puede considerar manipulación. Si se cambiara el ejemplo para registrar el seguimiento de la pila usando log4j en lugar de escribir en la consola, ¿eso lo haría aceptable? No hay mucho cambio, en mi opinión.
El problema real es qué se considera un procedimiento de recuperación excepcional y aceptable. Si no puede recuperarse de la excepción, lo mejor que puede hacer es informar el error.
fuente
Por favor, nunca, nunca, envuelva una excepción marcada en una excepción no marcada.
Si se encuentra lidiando con excepciones que cree que no debería, entonces mi consejo es que probablemente esté trabajando en el nivel de abstracción incorrecto.
Explicaré: las excepciones marcadas y no marcadas son dos bestias muy diferentes. Las excepciones marcadas son similares al antiguo método de devolver códigos de error ... sí, son algo más dolorosos de manejar que los códigos de error, pero también tienen ventajas. Las excepciones no verificadas son errores de programación y fallas críticas del sistema ... en otras palabras, excepciones excepcionales. Al tratar de explicar qué es una excepción, mucha gente se mete en un lío completo porque no reconoce la diferencia en estos dos casos muy diferentes.
fuente
FooException
, la situación actual no coincidirá con lo que esperaría la persona que llama en sentido ascendente cuandoFooException
se lanza a.Porque aún no han aprendido 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); } } }
fuente
El verdadero punto 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 mediante códigos de error, donde el código de manejo de errores se encuentra disperso por todas partes y cada llamada que pueda fallar se revisará para el código de retorno.
Si la excepción representa un error (que es la mayoría de los casos), por lo general, la forma más razonable de manejarlo es rescatar y dejar el manejo a una capa superior. Se debe considerar la posibilidad de volver a generar una excepción diferente si se le agrega alguna semántica significativa, es decir, este error es una falla inusual del sistema / problema temporal (red) / este es un error del lado del cliente o del servidor, etc.
De todas las estrategias de manejo de errores, la más ignorante es esconderse o simplemente imprimir un mensaje de error y seguir adelante como si nada.
La gente de Sun quería que el código fuera más explícito y obligó a los programadores a escribir qué excepciones se pueden producir mediante qué método. Parecía ser 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 de Java ignorantes, ahora tratan el manejo de excepciones como si fuera un error de lenguaje / "característica" que necesitaba una solución y escriben código de la peor o casi peor manera posible:
¿Cómo escribir de la "manera correcta" que?
fuente
Si desea que su excepción se maneje fuera del alcance del método actual, no necesita detectarla en realidad, sino que agrega la declaración 'throws' a la firma del método.
Las declaraciones try / catch que ha visto están solo en el código donde el programador decidió explícitamente manejar la excepción en su lugar y, por lo tanto, no lanzarla más.
fuente
Creo que los desarrolladores también tratan de considerar la importancia de "hacer lo correcto" en un contexto particular. A menudo, para el código desechable o cuando propagar la excepción hacia arriba no compraría nada porque la excepción es fatal, también podría ahorrar tiempo y esfuerzo "haciendo lo incorrecto".
fuente
Por lo general, se tragaría una excepción cuando no puede recuperarse, pero no es crítica. Un buen ejemplo es el IOExcetion que se puede lanzar al cerrar una conexión de base de datos. ¿Realmente quieres colapsar si esto sucede? Es por eso que los DBUtils de Yakarta tienen métodos closeSilently.
Ahora, para la excepción marcada que no puede recuperar pero que son críticas (generalmente debido a errores de programación), no se las trague. Creo que las excepciones deben registrarse lo más cerca posible de la fuente del problema, por lo que no recomendaría eliminar la llamada printStackTrace (). Querrá convertirlos en RuntimeException con el único propósito de no tener que declarar estas excepciones en su método comercial. Realmente no tiene sentido tener métodos comerciales de alto nivel como createClientAccount () arroja ProgrammingErrorException (lea SQLException), no hay nada que pueda hacer si tiene errores tipográficos en su sql o accede a índices incorrectos.
fuente
Por experiencia, tragar una excepción es perjudicial principalmente cuando no está impresa. Ayuda a llamar la atención si falla, y lo haré deliberadamente a veces, pero simplemente imprimir la excepción y continuar le permite encontrar el problema y solucionarlo, y sin embargo, por lo general, no afecta negativamente a otros que trabajan en la misma base de código. .
De hecho, me gusta Fail-Fast / Fail-HARD, pero al menos imprímelo. Si realmente "come" una excepción (que es realmente no hacer nada: {}) le costará DÍAS a alguien encontrarla.
El problema es que Java te obliga a atrapar muchas cosas que el desarrollador sabe que no se lanzarán o que no le importa si lo son. El más común es Thread.sleep (). Me doy cuenta de que hay agujeros que podrían permitir problemas de enhebrado aquí, pero generalmente sabes que no lo estás interrumpiendo. Período.
fuente
Puede haber muchas razones por las que uno usaría catch Exception. En muchos casos, es una mala idea porque también detecta RuntimeExceptions, y bueno, ¿no sabe en qué estado estarán los objetos subyacentes después de que esto suceda? Eso es siempre lo difícil con condiciones inesperadas: puede confiar en que el resto del código no fallará después.
Su ejemplo imprime el seguimiento de pila para que al menos sepa cuál podría haber sido la causa raíz. En proyectos de software más grandes, es mejor registrar estas cosas. Y esperemos que el componente de registro no arroje excepciones, ya que podría terminar en un bucle infinito (que probablemente matará su JVM).
fuente
Si tiene una excepción marcada y no quiere manejarla en un método, debería hacer que el método arroje la excepción. Solo detecta y maneja excepciones si vas a hacer algo útil con él. En mi libro, solo registrarlo no es muy útil, ya que los usuarios rara vez tienen tiempo para leer registros en busca de excepciones o saber qué hacer si se produce una excepción.
Si bien envolver la excepción es una opción, no le sugiero que lo haga a menos que; está lanzando una excepción diferente para que coincida con una interfaz existente o realmente no hay forma de que se lance dicha excepción.
Por cierto: si desea volver a lanzar una excepción marcada, puede hacerlo con
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 }
Nota: si hace esto, debe asegurarse de que la declaración throws para el método sea correcta, ya que el compilador no puede hacerlo por usted en esta situación.
fuente
Thread#stop()
ha sido obsoleto y deshabilitado mientras tanto (arrojaUnsupportedOperationException
).porque es una buena práctica. Pensé que todos lo sabían.
pero la fría verdad es que nadie entiende realmente cómo trabajar con excepciones. El estilo de manejo de errores de C tenía mucho más sentido.
fuente