Extensión de los tipos de excepción estándar
- Extender de RuntimeException o una de sus subclases en caso de definir un tipo de error no recuperable (Excepción no comprobada).
- Extender de Exception o una de sus subclases (a excepción de RuntimeException) en caso de definir un tipo de error recuperable (Excepción comprobada) que debe ser explícitamente declarado y capturado.
- No extender directamente de la clase Throwable, ya que la mayoría de tratamientos de errores mediante bloques try/catch se hace como mínimo a nivel de Exception. En estos casos, las excepciones derivadas directamente de Throwable no serían capturadas por estos bloques lo que podría provocar efectos no previstos.
Tratamiento y propagación de excepciones
Propagación entre métodos
Cuándo se gestionan las excepciones, hay ocasiones que se desea relanzar una excepción que está siendo gestionada. Un programador novicio cree que el código siguiente puede hacer esto:
public class EjemploExceptionRethrow { public static void demoRethrow()throws IOException { try { // Se fuerza el lanzamiento de una IOException cómo ejemplo, // Normalmente la excepción es disparada por la ejecución de código. throw new IOException(“Error”); } catch(Exception exception) { /* Se trata la excepción y se relanza */ throw exception; } } public static void main(String[] args) { try { demoRethrow(); } catch (IOException exception) { System.err.println(exception.getMessage()); } } }
El código anterior no compilará correctamente en caso de hacerse con versiones de Java anteriores a 7 ya que el método demoRethrow explicitamente especifica en su firma que la excepción lanzada es de tipo IOException, mientras que el bloque catch declara la excepción capturada y relanzada de tipo Exception. En este caso el compilador no es capaz de inferir el tipo real de la excepción. En caso de la compilación del código se realice sobre la versión 7 o posterior, el código anterior será válido y compilará correctamente. En este caso, el compilador sí es capaz de inferir el tipo final al que pertenece de la excepción relanzada analizando las excepciones lanzadas dentro del bloque try.
public class EjemploAntiguoExceptionRethrow { public static demoRethrow() { try { throw new IOException("Error"); } catch(IOException exception) { /* * Se trata la excepción y se relanza */ throw new RuntimeException(exception); } } public static void main(String[] args) { try { demoRethrow(); } catch (RuntimeException exception) { System.err.println(exception.getCause().getMessage()); } } }
Captura de excepciones mediante bloque try/catch/finally
Java 7 permite capturar múltiples excepciones en un mismo bloque catch. Este mecanismo es llamado multicatch.
try { // Ejecución que puede generar uno de los errores catch } catch(SQLException e) { System.out.println(e); } catch(IOException e) { System.out.println(e); } catch(Exception e) { System.out.println(e); }
A partir de Java 7 se puede implementar el multicatch.
try { // Ejecución que puede generar uno de los errores catch } catch(SQLException | IOException e) { System.out.println(e); } catch(Exception e) { System.out.println(e); }
A destacar la utilización del carácter pipe » | » para separar los nombres de las clases. El carácter pipe » | » entre los nombres de las excepciones es el mecanismo cómo se declaran las múltiples excepciones a ser capturadas por el mismo catch.
Bloque try with resources
- Diferencia de que entre paréntesis se declaran aquellos recursos que se desean proteger.
- Un recurso siempre debe ser cerrado después de que termine el programa.
- Un recurso es cualquier objeto que implemente la clase java.lang.AutoCloseable.
Ejemplo para definir un nuevo objeto TryWithResources
package TryWithResources; public class NewResource implements AutoCloseable{ String closingMessage; public NewResource(String closingMessage) { this.closingMessage = closingMessage; } public void doSomeWork(String work) throws ExceptionA{ System.out.println(work); throw new ExceptionA("Exception thrown while doing some work"); } @Override public void close() throws ExceptionB{ System.out.println(closingMessage); throw new ExceptionB("Exception thrown while closing"); } public void doSomeWork(NewResource res) throws ExceptionA{ res.doSomeWork("Wow res getting res to do work"); } }
Ejemplo de su utilización en la ejecución de un programa
package TryWithResources; public class TryWithRes { public static void main(String[] args) { try(NewResource res = new NewResource("Res1 closing")) { res.doSomeWork("Listening to podcast"); } catch(Exception e) { System.out.println("Exception: "+e.getMessage() +" Thrown by: "+e.getClass().getSimpleName()); } } }
Ejemplo cuándo existen recursos anidados ambos derivando de AutoCloseable.
package TryWithResources; public class TryWithResV2 { public static void main(String[] args) { try (NewResource res = new NewResource("Res1 closing"); NewResource res2 = new NewResource("Res2 closing")) { try (NewResource nestedRes = new NewResource("Nestedres closing")) { nestedRes.doSomeWork(res2); } } catch (Exception e) { System.out.println("Exception: " + e.getMessage() + " Thrown by: " + e.getClass().getSimpleName()); } } }
Nótese el uso del carácter » ; » cómo separador en la declaración de los recursos dentro del bloque try-with-resources.
Tratamiento de excepciones no capturadas
public class MultiplexUncaughtExceptionHandler implements UncaughtExceptionHandler { private final UncaughtExceptionHandler[] handlers; public MultiplexUncaughtExceptionHandler(UncaughtExceptionHandler... handlers) { super(); this.handlers = Arrays.copyOf(handlers, handlers.length); } public void uncaughtException(Thread t, Throwable e) { for (UncaughtExceptionHandler handler : handlers) { try { handler.uncaughtException(t, e); } catch (Throwable th) { th.printStackTrace(); } } } }
Se puede encontrar más información en este enlace oficial de Oracle.