Pregunta Java: explicación de excepción comprobada frente a desmarcada


He leído varias publicaciones en StackOverFlow acerca de las excepciones marcadas frente a las no verificadas. Sinceramente, todavía no estoy muy seguro de cómo usarlos correctamente.

Joshua Bloch en "Java efectivo" dijo eso

Use excepciones marcadas para   condiciones recuperables y tiempo de ejecución   excepciones para errores de programación   (Artículo 58 en la 2da edición)

Veamos si entiendo esto correctamente.

Aquí está mi entendimiento 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. ¿Es lo anterior considerado una excepción marcada?

2. ¿RuntimeException es una excepción sin marcar?

Aquí está mi entendimiento de una excepción no verificada:

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 también ser una excepción marcada? Puedo tratar de recuperar la situación de esta manera? ¿Puedo? (Nota: mi tercera pregunta está dentro del catch encima)

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 suba la excepción? ¿No está manejando el error más rápido? ¿Por qué burbujear?

EDIT: ¿Debería crear la excepción exacta o enmascararla usando Exception?

A continuación están mis lecturas

En Java, ¿cuándo debería crear una excepción marcada y cuándo debería ser una excepción de tiempo de ejecución?

Cuándo elegir las excepciones marcadas y no marcadas


582
2018-05-24 19:38


origen


Respuestas:


Mucha gente dice que las excepciones comprobadas (es decir, que debes atrapar o volver a lanzar explícitamente) no se deben usar en absoluto. Fueron eliminados en C # por ejemplo, y la mayoría de los idiomas no los tienen. Entonces siempre puedes lanzar una subclase de RuntimeException (excepción no verificada)

Sin embargo, creo que las excepciones controladas son útiles: se usan cuando se quiere forzar al usuario de su API a pensar cómo manejar la situación excepcional (si es recuperable). Es solo que las excepciones comprobadas se usan en exceso en la plataforma Java, lo que hace que la gente las odie.

Aquí está mi vista extendida sobre el tema.

En cuanto a las preguntas particulares:

  1. Es el NumberFormatException considerar una excepción marcada?
    No. NumberFormatException está desmarcado (= es la subclase de RuntimeException) ¿Por qué? No lo sé. (pero debería haber habido un método isValidInteger(..))

  2. Es RuntimeException una excepción no verificada?
    Sí exactamente.

  3. ¿Qué debería hacer aquí?
    Depende de dónde está este código y lo que quiere que suceda. Si está en la capa UI, tómalo y muestra una advertencia; si está en la capa de servicio, no lo capte del todo, déjelo burbujear. Simplemente no tragues la excepción. Si se produce una excepción en la mayoría de los casos, debe elegir uno de estos:

    • iniciar sesión y regresar
    • volver a lanzarlo (declarar que fue arrojado por el método)
    • construir una nueva excepción pasando el actual en constructor
  4. Ahora, ¿no podría el código anterior también ser una excepción marcada? Puedo tratar de recuperar la situación de esta manera? ¿Puedo?
    Pudo haber sido. Pero nada le impide atrapar la excepción no verificada también

  5. ¿Por qué las personas agregan clase Exception en la cláusula throws?
    En la mayoría de los casos, las personas son perezosas para considerar qué capturar y qué relanzar. Lanzamiento Exceptiones una mala práctica y debe evitarse.

Desafortunadamente, no existe una regla única que le permita determinar cuándo atrapar, cuándo volver a lanzar, cuándo usar marcado y cuándo usar las excepciones no verificadas. Estoy de acuerdo, esto causa mucha confusión y muchos códigos malos. El principio general lo establece Bloch (usted citó una parte). Y el principio general es volver a lanzar una excepción a la capa donde puede manejarlo.


389
2018-05-24 19:49



Si algo es una "excepción marcada" no tiene nada que ver con si lo capturas o con lo que haces en el bloque catch. Es una propiedad de clases de excepción. Cualquier cosa que sea una subclase de Exception  excepto para RuntimeException y sus subclases son una excepción comprobada.

El compilador de Java lo obliga a capturar excepciones comprobadas o declararlas en la firma del método. Se suponía que debía mejorar la seguridad del programa, pero la opinión mayoritaria parece ser que no vale la pena los problemas de diseño que crea.

¿Por qué dejan que la burbuja de excepción   ¿arriba? No se maneja el error cuanto antes   ¿mejor? ¿Por qué burbujear?

Porque esa es toda la punto de excepciones Sin esta posibilidad, no necesitarías excepciones. Le permiten manejar los errores en el nivel que elija, en lugar de obligarlo a tratarlos en métodos de bajo nivel donde ocurrieron originalmente.


205
2018-05-24 19:53



  1. ¿Es lo anterior considerado una excepción marcada? No El hecho de que esté manejando una excepción no lo convierte en una Excepción Controlada si se trata de una RuntimeException.

  2. ¿RuntimeException es una excepción no verificada? Sí

Las excepciones marcadas son subclases de java.lang.Exception Las excepciones no verificadas son subclases de java.lang.RuntimeException

Las llamadas que lanzan excepciones controladas deben estar encerradas en un bloque try {} o manejadas 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 las personas que llaman puedan hacer arreglos apropiados para manejar la excepción.

Espero que esto ayude.

Q: ¿debería burbujear el exacto   ¿Excepción o máscara con Excepción?

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. Debería crear una excepción personalizada y envolverla. Pero, y uno grande: nunca oscurecer en la causa original subyacente. Por ejemplo, Dont ever hacer seguimiento -

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException("Cannot login!!"); //<-- Eat away original root cause, thus obscuring underlying problem.
}

En lugar de hacer lo siguiente:

try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!.
}

Al consumir los entuertos de origen raíz originales, la causa real más allá de la recuperación es una pesadilla para los equipos de soporte de producción, donde todos a los que se les da acceso son registros de aplicaciones y mensajes de error. Aunque este último es un mejor diseño, muchas personas no lo utilizan a menudo porque los desarrolladores simplemente no transmiten el mensaje subyacente a la persona que llama. Así que haz una nota firme: Always pass on the actual exception ya sea que esté envuelto o no en alguna excepción específica de la aplicación.

En try-catching RuntimeExceptions

RuntimeExceptions como regla general no debe ser try-catched. Por lo general, señalan un error de programación y deben dejarse en paz. En cambio, el programador debe verificar la condición de error antes de invocar algún código que pueda dar como resultado una RuntimeException. Por ejemplo:

try {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!);
} catch (NullPointerException npe) {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

Esta es una mala práctica de programación. En cambio, se debería haber hecho un cheque nulo como ...

if (userObject != null) {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!);
} else {
   sendError("Sorry, your userObject was null. Please contact customer care.");
}

Pero hay momentos en que tal comprobación de errores es costosa, como el formato de número, considere esto:

try {
    String userAge = (String)request.getParameter("age");
    userObject.setAge(Integer.parseInt(strUserAge));
} catch (NumberFormatException npe) {
   sendError("Sorry, Age is supposed to be an Integer. Please try again.");
}

En este caso, la comprobación de errores previos a la invocación no vale la pena, ya que básicamente 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 simplemente acabar con try-catch.

Por lo tanto, NullPointerException y NumberFormatException son ambas RuntimeExceptions, la captura de una NullPointerException debe reemplazarse por una elegante null-check, mientras que yo recomiendo atrapar una NumberFormatException explícitamente para evitar la posible introducción de código propenso a errores.


61
2018-05-24 19:50



1. Si no está seguro acerca de una excepción, verifique la API:

 java.lang.Object
 extended by java.lang.Throwable
  extended by java.lang.Exception
   extended by java.lang.RuntimeException  //<-NumberFormatException is a RuntimeException  
    extended by java.lang.IllegalArgumentException
     extended by java.lang.NumberFormatException

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 es ya una excepción marcada

5. Si se espera que el método de llamada someMethod para atrapar la excepción, esta última puede ser arrojada. Simplemente "pasa la pelota". Un ejemplo de su uso sería si desea implementarlo en sus propios métodos privados y, en su lugar, manejar la excepción en su método público.

Una buena lectura es el documento de Oracle en sí mismo: http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

¿Por qué los diseñadores decidieron forzar un método para especificar todas las excepciones comprobadas no detectadas que se pueden arrojar dentro de su alcance? Cualquier excepción que pueda arrojarse mediante un método es parte de la interfaz de programación pública del método. Aquellos que llaman a un método deben conocer las excepciones que un método puede arrojar para que puedan decidir qué hacer con ellos. Estas excepciones son tanto una parte de la interfaz de programación de ese método como sus parámetros y valor de retorno.

La siguiente pregunta podría ser: "Si es tan bueno documentar la API de un método, incluidas las excepciones que puede arrojar, ¿por qué no especificar excepciones de tiempo de ejecución también?" Las excepciones de tiempo de ejecución representan problemas que son el resultado de un problema de programación y, como tal, no se puede esperar razonablemente que el código del cliente API se recupere de ellos o que los maneje de ninguna manera. Dichos problemas incluyen excepciones aritméticas, como dividir por cero; excepciones de puntero, como intentar acceder a un objeto a través de una referencia nula; e indexar excepciones, como intentar acceder a un elemento de matriz a través de un índice que es demasiado grande o demasiado pequeño.

También hay un poco de información importante en Especificación del lenguaje Java:

Las clases de excepciones comprobadas nombradas en la cláusula throws son parte del contrato entre el implementador y el usuario del método o constructor.

La conclusión en mi humilde opinión es que poder coger cualquier RuntimeException, pero no está obligado a hacerlo y, de hecho, la implementación no es necesaria para mantener lanzadas las mismas excepciones no verificadas, ya que esas no son parte del contrato.


15
2018-05-24 20:03



1) No, una excepción NumberFormatException es una excepción no seleccionada. A pesar de que lo atrapó (no es obligatorio), no está marcado. Esto se debe a que es una subclase de IllegalArgumentException que es una subclase de RuntimeException.

2) RuntimeException es la raíz de todas las Excepciones no verificadas. Cada subclase de RuntimeException está desmarcada. Todas las demás Excepciones y Throwables están marcadas, excepto los Errores (que vienen bajo Throwable).

3/4) Podría alertar al usuario de que eligieron un archivo inexistente y pedir uno nuevo. O simplemente deje de informar al usuario que ingresó algo inválido.

5) Lanzar y atrapar 'Excepción' 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 manejarlo. Por ejemplo, si escribió una biblioteca para manejar la lectura de algunos archivos y su método pasó un archivo inexistente, no tiene idea de cómo manejarlo. ¿La persona que llama desea volver a preguntar o dejar de fumar? Entonces le arrojas la Excepción a la cadena de regreso a la persona que llama.

En muchos casos, se produce una excepción no verificada porque el programador no verificó las entradas (en el caso de NumberFormatException en su primera pregunta). Es por eso que es opcional atraparlos, porque hay formas más elegantes de evitar generar esas excepciones.


9
2018-05-24 19:56



Controlado: propenso a suceder. Checked in Tiempo de compilación.

Por ejemplo ... FileOperations

No comprobado: debido a datos incorrectos. Controlado en tiempo de ejecución.

P.ej..

String s = "abc";
Object o = s;
Integer i = (Integer) o;

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at Sample.main(Sample.java:9)

Aquí la excepción se debe a datos incorrectos y de ninguna manera se puede determinar durante el tiempo de compilación.


7
2017-07-24 16:58



La JVM verifica las excepciones comprobadas en tiempo de compilación y su relación con los recursos (files / db / stream / socket, etc.). El motivo de la excepción comprobada 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 que pueden dar lugar a excepciones en el tiempo de ejecución. Es absolutamente correcto manipular / capturar excepciones sin marcar en el código.

Explicación tomada de http://coder2design.com/java-interview-questions/


5
2017-07-13 13:13



Solo quiero agregar un razonamiento para no usar excepciones marcadas. Esta no es una respuesta completa, pero creo que responde una parte de su pregunta y complementa muchas otras respuestas.

Siempre que estén marcadas las excepciones, hay una throws CheckedException en algún lugar de una firma de método (CheckedException podría ser cualquier excepción marcada). Una firma NO arroja una Excepción, arrojando Excepciones es un aspecto de la implementación. Interfaces, firmas de métodos, clases principales, todas estas cosas NO deberían depender de sus implementaciones. El uso de Excepciones marcadas aquí (en realidad, el hecho de que debe declarar el throws en la firma del método) vincula sus interfaces de nivel superior con sus implementaciones de estas interfaces.

Dejame mostrarte un ejemplo.

Tengamos una interfaz agradable y limpia como esta

public interface IFoo {
    public void foo();
}

Ahora podemos escribir muchas implementaciones de método foo(), como estas

public class Foo implements IFoo {
    @Override
    public void foo() {
        System.out.println("I don't throw and exception");
    }
}

Class Foo está perfectamente bien. Ahora hagamos un primer intento en Class Bar

public class Bar implements IFoo {
    @Override
    public void foo() {
        //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same.
        throw new InterruptedException();
    }
}

Esta clase Bar no compilará. Como InterruptedException es una excepción comprobada, debes capturarla (con un try-catch dentro del método foo ()) o declarar que la estás 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 que pueda tratarla en otro lugar), modifiquemos la firma.

public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}

¡Esta clase Bar no compilará tampoco! El método de barra foo () NO anula el método de IFoo foo () 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, como foo = new Bar();. Si el método de Bar foo () no anula el método fooo de IFoo, cuando lo haga foo.foo(); no llamará a la implementación de Bar de foo ().

Para hacer Bar's public void foo() throws InterruptedException anular IFoo public void foo() DEBO agregar throws InterruptedException a la firma del método IFoo. Esto, sin embargo, 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 agregué throws InterruptedException al método de Foo foo () Me gustaría obtener otro error que indica que el método de Foo foo () declara que arroja una excepción InterruptedException pero nunca arroja una excepción InterruptedException.

Como puede ver (si hice un trabajo decente al explicar esto), el hecho de que estoy lanzando una excepción comprobada como InterruptedException me obliga a vincular mi interfaz IFoo a una de sus implementaciones, lo que a su vez causa estragos en IFoo's otras implementaciones!

Esta es una gran razón por la cual las excepciones comprobadas son MALAS. En mayúsculas.

Una solución es capturar la excepción comprobada, envolverla en una excepción sin marcar y lanzar la excepción sin marcar.


3
2017-08-17 13:30



Para responder a la pregunta final (las otras parecen haber sido respondidas minuciosamente arriba), "¿Debo crear la excepción exacta o enmascararla usando Excepción?"

Supongo que quieres decir algo como esto:

public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}

No, siempre declara el más preciso excepción 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 no se encuentre el archivo; la persona que llama deberá manejar eso inteligentemente. Lanzar "Excepción" significa "Oye, sucede sh * t. Deal". Que es una API muy pobre.

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 los códigos "normales" que jamás escribirás.


2
2017-10-01 21:37



  • Java distingue dos categorías de excepciones (marcadas y sin marcar)
  • Java impone un requisito de captura o declarado para excepciones marcadas
  • El tipo de una excepción determina si una excepción está marcada o no.
  • Todos los tipos de excepciones que son subclases directas o indirectas de la clase RuntimeException son una excepción sin marcar.
  • Todas las clases que heredan de la clase Exception pero no RuntimeException se consideran excepciones marcadas.
  • Las clases que heredan de la clase Error se consideran desmarcadas.
  • El compilador comprueba cada llamada y desaceleración del método para determinar si el método arroja la excepción marcada.
    • Si es así, el compilador asegura que la excepción se detecta o se declara en una cláusula throws.
  • Para satisfacer la parte de declarar del requisito de capturar o declarar, el método que genera la excepción debe proporcionar una cláusula throws que contenga la excepción marcada.
  • Las clases de excepción se definen para verificarse cuando se consideran lo suficientemente importantes como para detectarlas o declararlas.

2
2017-10-02 20:21



¿Por qué dejan que suba la excepción? ¿No está manejando el error más rápido? ¿Por qué burbujear?

Por ejemplo, digamos que tienes algunos aplicación cliente-servidor y el cliente solicitó algún recurso que no se pudo encontrar o por otro error que pudo haber ocurrido en el servidor mientras se procesaba la solicitud del usuario, entonces es deber del servidor decirle al cliente por qué no pudo hacerlo. Obtener lo que solicitó, para lograr eso en el lado del servidor, el código se escribe para lanzar la excepción usando lanzar palabra clave 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.


2
2017-08-04 10:22