Comprender las excepciones marcadas y no marcadas en Java


703

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 catchanterior)

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

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 excepciones marcadas y no marcadas


66
Tengo un gran ejemplo de una excepción no verificada. Tengo una DataSeriesclase que contiene datos que siempre deben permanecer en orden basado en el tiempo. Hay un método para agregar un nuevo DataPointal final de a DataSeries. Si todo mi código funciona correctamente durante todo el proyecto, DataPointnunca 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.
Erick Robertson

3
Para agregar aún más confusión. Muchas personas abogaban por las excepciones comprobadas hace ~ 10 años, pero la opinión actual se está moviendo cada vez más hacia "las excepciones comprobadas son malas". (Sin embargo, no estoy de acuerdo con eso)
Kaj

10
Solo es útil para manejar una excepción cuando tiene algo útil que hacer con ella, de lo contrario, debe dejar que la persona que llama la maneje. Registrarlo y fingir que no sucedió generalmente no es útil. Solo volver a tirarlo no tiene sentido. Ajustar en una RuntimeException no es tan útil como algunos piensan, solo hace que el compilador deje de ayudarlo. (En mi humilde opinión)
Peter Lawrey

40
Deberíamos dejar de usar los términos completamente engañosos de las excepciones marcadas / no marcadas . Deberían llamarse excepciones de verificación obligatoria frente a verificación sin obligación .
Beato Geek

3
También he pensado en tu quinto punto public void method_name throws Exception {} ¿por qué algunas personas hacen eso?
Maveň ツ

Respuestas:


474

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:

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

  2. ¿Es RuntimeExceptionuna excepción no verificada?
    Sí exactamente.

  3. ¿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:

    • iniciar sesión y volver
    • volver a tirarlo (declararlo por el método)
    • construir una nueva excepción pasando la actual en el constructor
  4. 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

  5. ¿Por qué la gente agrega clase Exceptionen la cláusula throws?
    Muy a menudo porque las personas son flojas para considerar qué atrapar y qué volver a lanzar. Lanzar Exceptiones 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.


36
Con respecto a lanzar la excepción, no siempre es porque la gente es perezosa, también es común que, cuando implemente marcos, permita que los usuarios del marco puedan lanzar cualquier excepción. Puede, por ejemplo, verificar la firma de la interfaz invocable en JSE
Kaj

10
@Kaj: sí, cosas generales como Llamable, interceptores y similares son casos especiales. Pero en la mayoría de los casos es porque la gente es perezosa :)
Bozho

77
re: 3.1 "inicie sesión y regrese" Hágalo juiciosamente. Esto está muy cerca de comer o esconderse y una excepción. Haría esto por algo que no indique un problema, que no sea realmente excepcional. Los registros se inundan e ignoran con demasiada facilidad.
Chris

44
"cuando quiere obligar al usuario de su API a pensar cómo manejar la situación excepcional", no puede obligar a nadie a pensar si no quiere. Si no quieren pensar, escribirán un bloque de excepción pobre que no hace nada, o peor aún, elimina o interfiere con la información de error crítico. Es por eso que las excepciones marcadas son un fracaso.
adrianos

3
@adrianos "... no puedes obligar a nadie a pensar si no quieren ..." Con esta línea de pensamiento también podríamos eliminar errores de compilación ... Realmente no estoy apuntando a ti, he escuchado esto argumento una y otra vez y todavía considera que es la explicación más pobre posible para etiquetar Excepciones marcadas como un error. Como nota al margen, he visto antes un lenguaje en el que la compilación (y los errores de tiempo de ejecución también se hicieron efectivamente imposibles por el diseño). Ese camino condujo a algunos lugares muy oscuros.
Newtopian

233

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 el RuntimeExceptiony 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.

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

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.


3
¡Gracias! Ocasionalmente elimino excepciones de mis métodos debido al principio crap in crape out. Uno de los desarrolladores de mi equipo quiere ingresar una expresión xpath inválida, depende de ellos tratar con la excepción. En el improbable caso de que encuentren una excepción y no hagan nada, escucharán al respecto en la revisión del código.
jeremyjjbrown

12
"Cualquier cosa que sea una subclase de Throwable, excepto RuntimeException y sus subclases, es una excepción marcada". - Tu declaración es incorrecta. El error también hereda Throwable y no está marcado.
Bartzilla

8
@JonasEicher: básicamente, una de las principales ventajas de las excepciones es que le permiten elegir en qué parte de la pila de llamadas desea manejar los errores, que a menudo es bastante alto, mientras mantiene las capas intermedias completamente libres de artefactos de manejo de errores. Las excepciones marcadas destruyen exactamente esa ventaja. Otro problema es que la distinción marcada / no marcada está vinculada a la clase de excepción que también representa una categorización conceptual de excepciones, que combina dos aspectos que no están necesariamente relacionados en absoluto.
Michael Borgwardt

2
"Pero la opinión mayoritaria parece ser que no vale la pena los problemas de diseño que crea". - Cita por favor?
kervin

3
@Bartzilla Sí. Para completar, como Throwabledice el javadoc : "Throwable y cualquier subclase de Throwable que no sea también una subclase de RuntimeException o Error se consideran excepciones comprobadas"
Bart van Heukelom

74
  1. ¿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 Exceptionsi es un RuntimeException.

  2. Es RuntimeExceptionun unchecked exception? si

Checked Exceptionsson subclassesde java.lang.Exception Unchecked Exceptionsson subclassesdejava.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.

P: ¿debo hacer burbujear la excepción exacta o enmascararla usando Exception?

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 everhaz lo siguiente:

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

En su lugar, haga lo siguiente:

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

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 exceptionretroceda si está envuelto o no en una excepción específica de la aplicación.

Al intentar atrapar RuntimeExceptions

RuntimeExceptions 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 un RuntimeException. Por ej .:

try {
    setStatusMessage("Hello Mr. " + userObject.getName() + ", Welcome 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, un cheque nulo debería haberse hecho 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 dicha comprobación de errores es costosa, como el formato de números, 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.");
}

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, NullPointerExceptiony NumberFormatExceptionambos son RuntimeExceptions, la captura de un NullPointerExceptiondebería reemplazarse con una elegante comprobación nula, mientras que recomiendo capturar NumberFormatExceptionexplícitamente para evitar la posible introducción de código propenso a errores.


Gracias. Una pregunta más cuando estás burbujeando exception, ¿debería burbujear la excepción exacta o enmascararlo usando Exception? Escribo código encima de algún código heredado, y me Exceptionaparecen burbujas por todos lados. Me pregunto si este es el comportamiento correcto?
Thang Pham

1
Esta es una pregunta muy buena e importante, edité mi respuesta para incluir la explicación.
d-live

Muchas gracias. ¿Sería posible que me mostraras el contenido de LoginFailureException(sqle)?
Thang Pham

1
No tengo ningún código para esas cosas, acabo de inventar los nombres, etc. Si ves java.lang.Exception, tiene 4 constructores, dos de ellos aceptan java.lang.Throwable. En los fragmentos de arriba supuse que se LoginFailureExceptionextiende Exceptiony declara un constructorpublic LoginFailureException(Throwable cause) { super(cause) }
d-live

La mejor respuesta sobre el tema. Creo que las excepciones de tiempo de ejecución no deberían detectarse porque estas excepciones ocurren debido a la falta de una buena programación. Estoy completamente de acuerdo con la parte "Eliminar 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, donde todo lo que tienen acceso es registros de aplicaciones y mensajes de error".
huseyin

19

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

5) Si se espera que el método que llama someMethodpara 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

¿Por qué los diseñadores decidieron forzar un método para especificar todas las excepciones comprobadas no detectadas que se pueden lanzar dentro de su alcance? Cualquier excepción que pueda generar 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 puede lanzar un método para poder decidir qué hacer al respecto. Estas excepciones son tan 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 generar, ¿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 los maneje de ninguna manera. Tales 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 excepciones de indexación, 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 la Especificación del lenguaje Java :

Las clases de excepción marcadas 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 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.


Gracias. Una pregunta más cuando estás burbujeando exception, ¿debería burbujear la excepción exacta o enmascararlo usando Exception? Escribo código encima de algún código heredado, y me Exceptionaparecen burbujas por todos lados. Me pregunto si este es el comportamiento correcto?
Thang Pham

1
@Harry Dejaré que las personas con más conocimiento que yo respondan eso: stackoverflow.com/questions/409563/…
Aleadam

10

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 IllegalArgumentExceptioncual es una subclase de RuntimeException.

2) RuntimeExceptiones la raíz de todas las Excepciones no marcadas. Cada subclase de RuntimeExceptionestá desmarcada. Todas las demás excepciones y Throwablese verifican, excepto los errores (que se detallan a continuación Throwable).

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 Exceptionocurre porque el programador no verificó las entradas (en el caso de NumberFormatExceptionsu primera pregunta). Es por eso que es opcional atraparlos, porque hay formas más elegantes de evitar generar esas excepciones.


Gracias. Una pregunta más cuando estás burbujeando exception, ¿debería burbujear la excepción exacta o enmascararlo usando Exception? Escribo código encima de algún código heredado, y me Exceptionaparecen burbujas por todos lados. Me pregunto si este es el comportamiento correcto?
Thang Pham

Puede hacer que su método también arroje Excepción (que no es ideal). O captura Exception y lanza una mejor Excepción (como IOException o algo así). Todas las excepciones pueden tomar una excepción en su constructor como una 'causa', por lo que debe usar eso.
dontocsata

8

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..

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.


6

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/


5

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):

Aquí está la pauta final: si se puede esperar razonablemente que un cliente se recupere de una excepción, conviértalo en una excepción marcada. Si un cliente no puede hacer nada para recuperarse de la excepción, conviértalo en una excepción no verificada

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.


5

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:

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

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. Lanzar Exceptionsignifica "Hey, sh * t sucede. Trato". Lo cual es muy pobre API.

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 normalcódigos " " que escribirás.


Exactamente, haga que su declaración de excepción de cheque sea parte de la documentación de su código, así como ayudar a la persona que usa su software.
Salvador Valencia

5

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ó.


3

¿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.


Buen punto. Lo que significa es llevarlo a la capa superior más responsable que controla el flujo lógico y supervisa la lógica empresarial de la aplicación. Sería imposible, por ejemplo, que la capa de la base de datos comunique al cliente que falta algo crítico o que no responde. Cuando aparece en la capa superior del servidor, es sencillo actualizar la vista del cliente con un mensaje de error crítico.
Salvador Valencia


3

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 CheckedExceptionuna firma en algún lugar de un método ( CheckedExceptionpodrí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 declarar throwsen 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

public interface IFoo {
    public void foo();
}

Ahora podemos escribir muchas implementaciones de métodos foo(), como estos

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 la clase 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 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 InterruptedExceptiona 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.

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

¡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, like foo = new Bar();. Si el método foo () de Bar no anula el método foo de IFoo, cuando lo haga foo.foo();no llamará a la implementación de foo () de Bar.

Para public void foo() throws InterruptedExceptionanular los IFoo de Bar, public void foo()DEBO agregar throws InterruptedExceptiona 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 agrego throws InterruptedExceptional 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.


2
Sí, es malo porque dijiste que no querías atraparlo. Pero para evitar afectar la firma de IFOO, tendrá que hacerlo. Prefiero hacer eso y seguir adelante en lugar de reequilibrar todas las firmas de mis interfaces para evitar detectar una excepción (solo porque las excepciones son MALAS).
Salvador Valencia

Si estoy de acuerdo. Estaba un poco confuso sobre algo. Quiero que se propague una excepción, para poder tratarlo en otro lugar. Una solución es detectar la InterruptedException y generar una excepción no verificada. es decir, evitamos las excepciones marcadas y pasamos excepciones no verificadas (incluso si solo tienen sentido como envoltorio)
Blueriver

"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". Acabo de probar su idea, y es posible compilar incluso si agregamos throws InterruptedExceptiona 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étodo Exception, solo le da a una implementación la opción de lanzar o no lanzar una excepción (cualquier excepción, ya que Exceptionencapsula todas las excepciones).
Flyout91

Sin embargo, admito que puede ser confuso porque cuando implemente dicha interfaz y haga clic en algo como "Agregar métodos no implementados", se crearán automáticamente con la throw Exceptionclá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.
Flyout91

Esto pierde todo el punto. El propósito de una interfaz es declarar el contrato que un cliente requiere para ser satisfecho. Esto puede incluir escenarios de falla que puede manejar. Cuando una implementación encuentra un error, debe asignar ese error al fallo abstracto apropiado declarado por la interfaz del cliente.
erickson

3
  • Java distingue entre dos categorías de excepciones (marcadas y no marcadas).
  • 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 excepción que son directos o indirectos subclassesde clase RuntimeException son excepciones no verificadas.
  • Todas las clases que heredan de la clase Exceptionpero no RuntimeExceptionse consideran checked exceptions.
  • Las clases que heredan de la clase Error se consideran desmarcadas.
  • El compilador comprueba cada llamada al método y la desaceleración para determinar si el método arroja checked exception.
    • 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 throwscláusula que contenga el checked-exception.
  • Exception Las clases se definen para ser verificadas cuando se consideran lo suficientemente importantes como para atrapar o declarar.

2

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).


2

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.


2

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 RuntimeExceptiono Error.

En este video, explica las excepciones marcadas y no marcadas en Java:
https://www.youtube.com/watch?v=ue2pOqLaArw


1

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.


um si NumberFormatException es una excepción marcada, como usted dice, ¿no está contradiciendo el hecho de que se hereda de RuntimeException ?
Eis

Lo siento, no estaba muy claro. Me refería a FileNotFoundException, no a NumberFormatException. Basado en su # 2 y # 4, parecía que pensaba que Checked vs. Unchecked se basaba en cómo manejaste la excepción después de atraparla. No cómo se definió.
mamboking

-1

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


44
Porque solo la mayoría de las personas que llaman, no todas las personas que llaman, deben envolver y volver a lanzar. El hecho de que la excepción esté marcada significa que la persona que llama tiene que pensar si es una de las "más" personas que llaman o una minoría que puede y debe manejar la excepción.
Warren Dew

1
Si desea verificar los errores para cada llamada que realiza, "regrese" a C. Las excepciones son una forma de separar la ejecución normal del programa de la anormal, sin contaminar su código. Las excepciones aseguran que no puede ignorar un error silenciosamente en algún nivel .
Slawomir

-1

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.

  • Si no se manejan correctamente, darán un error de tiempo de compilación (no excepció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).


-2

Todas las excepciones deben ser marcadas excepciones.

  1. Las excepciones no marcadas son gotos sin restricciones. Y los gotos sin restricciones se consideran algo malo.

  2. 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.

  3. 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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.