Joshua Bloch en " Java efectivo " dijo que
Use excepciones marcadas para condiciones recuperables y excepciones de tiempo de ejecución para errores de programación (Elemento 58 en la 2da edición)
A ver si entiendo esto correctamente.
Aquí está mi comprensión de una excepción marcada:
try{
String userInput = //read in user input
Long id = Long.parseLong(userInput);
}catch(NumberFormatException e){
id = 0; //recover the situation by setting the id to 0
}
1. ¿Se considera lo anterior como una excepción marcada?
2. ¿RuntimeException es una excepción no verificada?
Aquí está mi comprensión de una excepción no marcada:
try{
File file = new File("my/file/path");
FileInputStream fis = new FileInputStream(file);
}catch(FileNotFoundException e){
//3. What should I do here?
//Should I "throw new FileNotFoundException("File not found");"?
//Should I log?
//Or should I System.exit(0);?
}
4. Ahora, ¿no podría el código anterior ser también una excepción marcada? ¿Puedo intentar recuperar la situación así? ¿Puedo? (Nota: mi tercera pregunta está dentro de lo catch
anterior)
try{
String filePath = //read in from user input file path
File file = new File(filePath);
FileInputStream fis = new FileInputStream(file);
}catch(FileNotFoundException e){
//Kindly prompt the user an error message
//Somehow ask the user to re-enter the file path.
}
5. ¿Por qué la gente hace esto?
public void someMethod throws Exception{
}
¿Por qué dejan que la excepción brote? ¿No es mejor manejar el error antes? ¿Por qué burbujear?
6. ¿Debo burbujear la excepción exacta o enmascararla usando Exception?
Abajo están mis lecturas
DataSeries
clase que contiene datos que siempre deben permanecer en orden basado en el tiempo. Hay un método para agregar un nuevoDataPoint
al final de aDataSeries
. Si todo mi código funciona correctamente durante todo el proyecto,DataPoint
nunca se debe agregar un al final que tenga una fecha anterior a la que ya está al final. Cada módulo en todo el proyecto está construido con esta verdad. Sin embargo, verifico esta condición y lanzo una excepción no marcada si sucede. ¿Por qué? Si sucede, quiero saber quién está haciendo esto y solucionarlo.Respuestas:
Mucha gente dice que las excepciones marcadas (es decir, las que debe capturar o volver a lanzar explícitamente) no deben utilizarse en absoluto. Se eliminaron en C #, por ejemplo, y la mayoría de los idiomas no los tienen. Por lo tanto, siempre puede lanzar una subclase de
RuntimeException
(excepción no marcada)Sin embargo, creo que las excepciones comprobadas son útiles: se usan cuando desea obligar al usuario de su API a pensar cómo manejar la situación excepcional (si es recuperable). Es solo que las excepciones marcadas se usan en exceso en la plataforma Java, lo que hace que las personas las odien.
Aquí está mi visión extendida sobre el tema .
En cuanto a las preguntas particulares:
¿Es el
NumberFormatException
considerar una excepción marcada?No.
NumberFormatException
no está marcado (= es una subclase deRuntimeException
). ¿Por qué? No lo sé. (pero debería haber habido un métodoisValidInteger(..)
)¿Es
RuntimeException
una excepción no verificada?Sí exactamente.
¿Qué debo hacer aquí?
Depende de dónde esté este código y de lo que quieras que suceda. Si está en la capa de la interfaz de usuario, atrápelo y muestre una advertencia; si está en la capa de servicio, no la atrape en absoluto, déjela burbujear. Simplemente no te tragues la excepción. Si ocurre una excepción en la mayoría de los casos, debe elegir uno de estos:
Ahora, ¿no podría el código anterior también ser una excepción marcada? ¿Puedo intentar recuperar la situación así? ¿Puedo?
Pudo haber sido. Pero nada le impide captar la excepción no verificada también
¿Por qué la gente agrega clase
Exception
en la cláusula throws?Muy a menudo porque las personas son flojas para considerar qué atrapar y qué volver a lanzar. Lanzar
Exception
es una mala práctica y debe evitarse.Por desgracia, no existe una regla única que le permita determinar cuándo atrapar, cuándo volver a lanzar, cuándo usar las excepciones marcadas y cuándo no controladas. Estoy de acuerdo en que esto causa mucha confusión y mucho código incorrecto. Bloch establece el principio general (usted citó una parte de él). Y el principio general es volver a lanzar una excepción a la capa donde puede manejarla.
fuente
Si algo es una "excepción marcada" no tiene nada que ver con si lo atrapa o qué hace en el bloque de captura. Es una propiedad de las clases de excepción. Todo lo que es una subclase de
Exception
excepción para elRuntimeException
y sus subclases es una excepción comprobada.El compilador de Java te obliga a capturar excepciones comprobadas o declararlas en la firma del método. Se suponía que mejoraría la seguridad del programa, pero la opinión mayoritaria parece ser que no vale la pena los problemas de diseño que crea.
Porque ese es todo el punto de excepciones. Sin esta posibilidad, no necesitaría excepciones. Le permiten manejar errores en el nivel que elija, en lugar de obligarlo a tratarlos en métodos de bajo nivel donde ocurren originalmente.
fuente
Throwable
dice el javadoc : "Throwable y cualquier subclase de Throwable que no sea también una subclase de RuntimeException o Error se consideran excepciones comprobadas"¿Se considera lo anterior como una excepción marcada? No El hecho de que esté manejando una excepción no lo convierte en un
Checked Exception
si es unRuntimeException
.Es
RuntimeException
ununchecked exception
? siChecked Exceptions
sonsubclasses
dejava.lang.Exception
Unchecked Exceptions
sonsubclasses
dejava.lang.RuntimeException
Las llamadas que arrojan excepciones comprobadas deben incluirse en un bloque try {} o manejarse en un nivel superior en la persona que llama del método. En ese caso, el método actual debe declarar que arroja dichas excepciones para que los llamantes puedan hacer los arreglos apropiados para manejar la excepción.
Espero que esto ayude.
R: Sí, esta es una muy buena pregunta y una importante consideración de diseño. La clase Exception es una clase de excepción muy general y se puede usar para ajustar excepciones internas de bajo nivel. Será mejor que crees una excepción personalizada y la envuelvas dentro. Pero, y uno grande: nunca oculte la causa raíz original subyacente. Por ejemplo,
Don't ever
haz lo siguiente:En su lugar, haga lo siguiente:
La eliminación de la causa raíz original entierra la causa real más allá de la recuperación es una pesadilla para los equipos de soporte de producción, a los que solo tienen acceso los registros de aplicaciones y los mensajes de error. Aunque este último es un mejor diseño, muchas personas no lo usan a menudo porque los desarrolladores simplemente no transmiten el mensaje subyacente a la persona que llama. Por lo tanto, tome una nota firme:
Always pass on the actual exception
retroceda si está envuelto o no en una excepción específica de la aplicación.RuntimeException
s como regla general no debe ser probado. Generalmente señalan un error de programación y deben dejarse solos. En cambio, el programador debe verificar la condición de error antes de invocar algún código que pueda resultar en unRuntimeException
. Por ej .:Esta es una mala práctica de programación. En cambio, un cheque nulo debería haberse hecho como:
Pero hay momentos en que dicha comprobación de errores es costosa, como el formato de números, considere esto:
Aquí la comprobación de errores previa a la invocación no vale la pena porque esencialmente significa duplicar todo el código de conversión de cadena a entero dentro del método parseInt (), y es propenso a errores si lo implementa un desarrollador. Por lo tanto, es mejor eliminar el try-catch.
Entonces,
NullPointerException
yNumberFormatException
ambos sonRuntimeExceptions
, la captura de unNullPointerException
debería reemplazarse con una elegante comprobación nula, mientras que recomiendo capturarNumberFormatException
explícitamente para evitar la posible introducción de código propenso a errores.fuente
exception
, ¿debería burbujear la excepción exacta o enmascararlo usandoException
? Escribo código encima de algún código heredado, y meException
aparecen burbujas por todos lados. Me pregunto si este es el comportamiento correcto?LoginFailureException(sqle)
?LoginFailureException
extiendeException
y declara un constructorpublic LoginFailureException(Throwable cause) { super(cause) }
1) Si no está seguro acerca de una excepción, verifique la API:
2) Sí, y cada excepción que lo extiende.
3) No hay necesidad de atrapar y lanzar la misma excepción. Puede mostrar un nuevo cuadro de diálogo de archivo en este caso.
4) FileNotFoundException ya es una excepción marcada.
5) Si se espera que el método que llama
someMethod
para atrapar la excepción, este último pueda ser lanzado. Simplemente "pasa la pelota". Un ejemplo de su uso sería si desea incluirlo en sus propios métodos privados y manejar la excepción en su método público.Una buena lectura es el documento de Oracle: http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html
También hay un poco de información importante en la Especificación del lenguaje Java :
La conclusión en mi humilde opinión es que puede atrapar cualquiera
RuntimeException
, pero no está obligado a hacerlo y, de hecho, la implementación no está obligada a mantener las mismas excepciones no verificadas, ya que no forman parte del contrato.fuente
exception
, ¿debería burbujear la excepción exacta o enmascararlo usandoException
? Escribo código encima de algún código heredado, y meException
aparecen burbujas por todos lados. Me pregunto si este es el comportamiento correcto?1) No, un NumberFormatException es una excepción no marcada. A pesar de que lo atrapó (no es obligatorio) porque no está marcado. Esto se debe a que es una subclase de la
IllegalArgumentException
cual es una subclase deRuntimeException
.2)
RuntimeException
es la raíz de todas las Excepciones no marcadas. Cada subclase deRuntimeException
está desmarcada. Todas las demás excepciones yThrowable
se verifican, excepto los errores (que se detallan a continuaciónThrowable
).3/4) Puede alertar al usuario de que eligió un archivo inexistente y solicitar uno nuevo. O simplemente deje de informar al usuario que ingresó algo no válido.
5) Lanzar y atrapar
'Exception'
es una mala práctica. Pero, en términos más generales, puede lanzar otras excepciones para que la persona que llama pueda decidir cómo lidiar con eso. Por ejemplo, si escribió una biblioteca para manejar la lectura de alguna entrada de archivo y su método pasó a un archivo inexistente, no tiene idea de cómo manejar eso. ¿La persona que llama quiere volver a preguntar o renunciar? Entonces arrojas la Excepción por la cadena de nuevo a la persona que llama.En muchos casos,
unchecked Exception
ocurre porque el programador no verificó las entradas (en el caso deNumberFormatException
su primera pregunta). Es por eso que es opcional atraparlos, porque hay formas más elegantes de evitar generar esas excepciones.fuente
exception
, ¿debería burbujear la excepción exacta o enmascararlo usandoException
? Escribo código encima de algún código heredado, y meException
aparecen burbujas por todos lados. Me pregunto si este es el comportamiento correcto?Marcado: propenso a suceder. Comprobado en tiempo de compilación.
Ej. FileOperations
Sin marcar: debido a datos incorrectos. Comprobado en tiempo de ejecución.
P.ej..
Aquí la excepción se debe a datos incorrectos y de ninguna manera se puede determinar durante el tiempo de compilación.
fuente
La JVM verifica las excepciones verificadas en tiempo de compilación y está relacionado con los recursos (archivos / db / stream / socket, etc.). El motivo de la excepción marcada es que, en el momento de la compilación, si los recursos no están disponibles, la aplicación debe definir un comportamiento alternativo para manejar esto en el bloque catch / finally.
Las excepciones no verificadas son errores puramente programáticos, cálculos incorrectos, datos nulos o incluso fallas en la lógica empresarial pueden conducir a excepciones de tiempo de ejecución. Está absolutamente bien manejar / capturar excepciones no verificadas en el código.
Explicación tomada de http://coder2design.com/java-interview-questions/
fuente
Mi descripción favorita absoluta de la diferencia entre las excepciones no marcadas y marcadas es proporcionada por el artículo del tutorial de Java Tutorial, " Excepciones no verificadas - la controversia " (perdón por tener todo lo básico en esta publicación, pero, oye, los conceptos básicos son a veces los mejores):
El corazón de "qué tipo de excepción lanzar" es semántico (hasta cierto punto) y la cita anterior proporciona una guía excelente (por lo tanto, todavía estoy impresionado por la idea de que C # se deshizo de las excepciones comprobadas, particularmente como Liskov argumenta a favor de su utilidad).
El resto se vuelve lógico: ¿a qué excepciones espera el compilador que responda, explícitamente? De los que espera que el cliente se recupere.
fuente
Para responder a la pregunta final (las otras parecen estar completamente respondidas anteriormente), "¿Debo burbujear la excepción exacta o enmascararla usando Exception?"
Supongo que quieres decir algo como esto:
No, siempre declare la excepción más precisa posible, o una lista de tales. Las excepciones que declara que su método es capaz de lanzar son parte del contrato entre su método y la persona que llama. Lanzar
"FileNotFoundException"
significa que es posible que el nombre del archivo no sea válido y que el archivo no se encuentre; la persona que llama tendrá que manejar eso de manera inteligente. LanzarException
significa "Hey, sh * t sucede. Trato". Lo cual es muy pobreAPI
.En los comentarios sobre el primer artículo hay algunos ejemplos donde "throws
Exception
" es una declaración válida y razonable, pero ese no es el caso para la mayoría de losnormal
códigos " " que escribirás.fuente
Excepciones de tiempo de ejecución : las excepciones de tiempo de ejecución se refieren como excepciones no verificadas. Todas las demás excepciones son excepciones marcadas y no se derivan de java.lang.RuntimeException.
Excepciones marcadas: una excepción marcada debe detectarse en algún lugar de su código. Si invoca un método que arroja una excepción marcada pero no detecta la excepción marcada en alguna parte, su código no se compilará. Es por eso que se llaman excepciones verificadas: el compilador verifica para asegurarse de que se manejan o declaran.
Varios de los métodos en la API de Java arrojan excepciones marcadas, por lo que a menudo escribirá controladores de excepciones para hacer frente a las excepciones generadas por métodos que no escribió.
fuente
¿Por qué dejan que la excepción brote? ¿No es mejor manejar el error antes? ¿Por qué burbujear?
Por ejemplo, supongamos que tiene alguna aplicación cliente-servidor y el cliente ha solicitado un recurso que no se pudo encontrar o por algún otro error que podría haber ocurrido en el lado del servidor al procesar la solicitud del usuario, entonces es el deber del servidor para decirle al cliente por qué no pudo obtener lo que solicitó, por lo que para lograr eso en el lado del servidor, el código se escribe para lanzar la excepción usando la palabra clave throw en lugar de tragarlo o manejarlo. si el servidor lo maneja / traga entonces, no habrá ninguna posibilidad de informar al cliente de qué error se ha producido.
Nota: Para dar una descripción clara de qué tipo de error ha ocurrido, podemos crear nuestro propio objeto Exception y lanzarlo al cliente.
fuente
Creo que las excepciones marcadas son un buen recordatorio para el desarrollador que usa una biblioteca externa de que las cosas pueden salir mal con el código de esa biblioteca en situaciones excepcionales.
Lea más sobre las excepciones marcadas frente a las no marcadas aquí http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/
fuente
Solo quiero agregar algún razonamiento para no usar excepciones marcadas en absoluto. Esta no es una respuesta completa, pero creo que responde parte de su pregunta y complementa muchas otras respuestas.
Siempre que hay excepciones verificadas, hay
throws CheckedException
una firma en algún lugar de un método (CheckedException
podría ser cualquier excepción marcada). Una firma NO arroja una Excepción, lanzar Excepciones es un aspecto de la implementación. Las interfaces, las firmas de métodos, las clases principales, todas estas cosas NO deberían depender de sus implementaciones. El uso de Excepciones marcadas aquí (en realidad el hecho de que tiene que declararthrows
en la firma del método) está vinculando sus interfaces de nivel superior con sus implementaciones de estas interfaces.Dejame mostrarte un ejemplo.
Tengamos una interfaz agradable y limpia como esta
Ahora podemos escribir muchas implementaciones de métodos
foo()
, como estosClass Foo está perfectamente bien. Ahora hagamos un primer intento en la clase Bar
Esta barra de clase no se compilará. Como InterruptedException es una excepción marcada, debe capturarla (con un método try-catch inside foo ()) o declarar que la está lanzando (agregando
throws InterruptedException
a la firma del método). Como no quiero capturar esta excepción aquí (quiero que se propague hacia arriba para poder tratarla adecuadamente en otro lugar), modifiquemos la firma.¡Esta barra de clase tampoco compilará! El método foo () de Bar NO anula el método foo () de IFoo ya que sus firmas son diferentes. Podría eliminar la anotación @Override, pero quiero programar contra la interfaz IFoo like
IFoo foo;
y luego decidir qué implementación quiero usar, likefoo = new Bar();
. Si el método foo () de Bar no anula el método foo de IFoo, cuando lo hagafoo.foo();
no llamará a la implementación de foo () de Bar.Para
public void foo() throws InterruptedException
anular los IFoo de Bar,public void foo()
DEBO agregarthrows InterruptedException
a la firma del método de IFoo. Sin embargo, esto causará problemas con mi clase Foo, ya que la firma del método foo () difiere de la firma del método IFoo. Además, si agregothrows InterruptedException
al método foo () de Foo, obtendría otro error que indica que el método foo () de Foo declara que arroja una InterruptedException, pero nunca arroja una InterruptedException.Como puede ver (si hice un trabajo decente al explicar esto), el hecho de que estoy lanzando una excepción marcada como InterruptedException me está obligando a vincular mi interfaz IFoo a una de sus implementaciones, lo que a su vez causa estragos en IFoo otras implementaciones!
Esta es una gran razón por la cual las excepciones marcadas son MALAS. En mayúsculas.
Una solución es capturar la excepción marcada, envolverla en una excepción no marcada y lanzar la excepción no marcada.
fuente
throws InterruptedException
a la firma del método de IFoo sin incluir nada en ninguna implementación. Por lo tanto, realmente no causa ningún problema. Si en una interfaz realiza cada lanzamiento de firma de métodoException
, solo le da a una implementación la opción de lanzar o no lanzar una excepción (cualquier excepción, ya queException
encapsula todas las excepciones).throw Exception
cláusula en su firma, a pesar de que su implementación no arrojará nada o puede ser Una excepción más específica. Pero todavía siento que es una buena práctica lanzar siempre la excepción para el método de una interfaz porque, nuevamente, le da al usuario la opción de lanzar o no lanzar nada.subclasses
de claseRuntimeException
son excepciones no verificadas.Exception
pero noRuntimeException
se consideranchecked exceptions
.checked exception
.throws
cláusula que contenga elchecked-exception
.Exception
Las clases se definen para ser verificadas cuando se consideran lo suficientemente importantes como para atrapar o declarar.fuente
Aquí hay una regla simple que puede ayudarlo a decidir. Está relacionado con la forma en que se utilizan las interfaces en Java.
Tome su clase e imagine diseñar una interfaz para ella de modo que la interfaz describa la funcionalidad de la clase pero ninguna de la implementación subyacente (como debería ser una interfaz). Suponga que tal vez podría implementar la clase de otra manera.
Mire los métodos de la interfaz y considere las excepciones que podrían lanzar:
Si un método puede generar una excepción, independientemente de la implementación subyacente (en otras palabras, solo describe la funcionalidad), entonces probablemente debería ser una excepción comprobada en la interfaz.
Si la implementación subyacente causa una excepción, no debería estar en la interfaz. Por lo tanto, debe ser una excepción no verificada en su clase (ya que las excepciones no verificadas no necesitan aparecer en la firma de la interfaz), o debe envolverla y volver a lanzarla como una excepción marcada que forma parte del método de la interfaz.
Para decidir si debe ajustar y volver a lanzar, debe considerar nuevamente si tiene sentido que un usuario de la interfaz tenga que manejar la condición de excepción de inmediato, o si la excepción es tan general que no hay nada que pueda hacer al respecto y debería propagar hasta la pila. ¿Tiene sentido la excepción envuelta cuando se expresa como funcionalidad de la nueva interfaz que está definiendo o es solo un proveedor de una bolsa de posibles condiciones de error que también podrían ocurrir con otros métodos? Si es lo primero, aún podría ser una excepción marcada, de lo contrario, debería estar desmarcado.
Por lo general, no debe planear "hacer burbujas" excepciones (atrapar y volver a lanzar). La persona que llama debe manejar una excepción (en cuyo caso está marcada) o debe llegar hasta un controlador de alto nivel (en cuyo caso es más fácil si no está marcada).
fuente
Solo para señalar que si arroja una excepción marcada en un código y la captura está a pocos niveles arriba, debe declarar la excepción en la firma de cada método entre usted y la captura. Entonces, la encapsulación está rota porque todas las funciones en la ruta de lanzamiento deben conocer los detalles de esa excepción.
fuente
En resumen, las excepciones que su módulo o módulos anteriores deben manejar durante el tiempo de ejecución se llaman excepciones verificadas; otros son excepciones no marcadas que son
RuntimeException
oError
.En este video, explica las excepciones marcadas y no marcadas en Java:
https://www.youtube.com/watch?v=ue2pOqLaArw
fuente
Todas esas son excepciones marcadas. Las excepciones no marcadas son subclases de RuntimeException. La decisión no es cómo manejarlos, es si su código los arroja. Si no desea que el compilador le diga que no ha manejado una excepción, entonces use una excepción no verificada (subclase de RuntimeException). Deben guardarse para situaciones de las que no puede recuperarse, como errores de falta de memoria y similares.
fuente
Si a alguien le interesa otra prueba más para que no le gusten las excepciones marcadas, consulte los primeros párrafos de la popular biblioteca JSON:
"Aunque esta es una excepción marcada, rara vez es recuperable. La mayoría de las personas que llaman simplemente deben envolver esta excepción en una excepción no marcada y volver a lanzar:"
Entonces, ¿por qué alguien haría que los desarrolladores sigan comprobando la excepción, si deberíamos "simplemente envolverlo"? jajaja
http://developer.android.com/reference/org/json/JSONException.html
fuente
Excepciones marcadas :
Las excepciones comprobadas por el compilador para la ejecución sin problemas del programa en tiempo de ejecución se denominan Excepción comprobada.
Estos ocurren en tiempo de compilación.
Todas las subclases de la clase de excepción, excepto RuntimeException, son excepción comprobada.
Ejemplo hipotético : suponga que está saliendo de su casa para el examen, pero si verifica si tomó su boleto Hall en casa (tiempo de compilación), entonces no habrá ningún problema en Exam Hall (tiempo de ejecución).
Excepción no marcada :
Las excepciones que no son verificadas por el compilador se llaman Excepciones no verificadas.
Estos ocurren en tiempo de ejecución.
Si estas excepciones no se manejan adecuadamente, no dan error de tiempo de compilación. Pero el programa terminará prematuramente en tiempo de ejecución.
Todas las subclases de RunTimeException y Error son excepciones no marcadas.
Ejemplo hipotético : suponga que se encuentra en la sala de examen pero de alguna manera su escuela tuvo un accidente de incendio (es decir, en tiempo de ejecución) donde no puede hacer nada en ese momento, pero se pueden tomar precauciones antes (tiempo de compilación).
fuente
Todas las excepciones deben ser marcadas excepciones.
Las excepciones no marcadas son gotos sin restricciones. Y los gotos sin restricciones se consideran algo malo.
Las excepciones no marcadas rompen la encapsulación. Para procesarlos correctamente, se deben conocer todas las funciones en el árbol de llamadas entre el lanzador y el receptor para evitar errores.
Las excepciones son errores en la función que los arroja pero no errores en la función que los procesa. El propósito de las excepciones es dar al programa una segunda oportunidad al diferir la decisión de si es un error o no a otro contexto. Solo en el otro contexto se puede tomar la decisión correcta.
fuente