Las excepciones son un mecanismo incorporado en algunos lenguajes como Java para el manejo de errores y condiciones de error. En la implementación de las excepciones en los lenguajes hay diferencias, por ejemplo, en Java hay excepciones checked y unchecked y en lenguajes como C# o Groovy todas las excepciones son consideradas unchecked. En cualquier caso son una mejor forma de forzar a gestionar las condiciones de error que se producen que el comprobar no obligatoriamente el valor de retorno de una función, incluso JavaScript incorpora excepciones.
Las palabras reservadas en Java para el manejo de excepciones son try, catch , finally, throw y throws. El manejo de algunas excepciones consiste en emitir su pila de llamadas o stacktrace en la terminal o en el sistema de logging. El stacktrace contiene un mensaje de error, los métodos de la pila de llamadas del thread que la causó junto con el número de la línea. Además, las excepciones puede tener asociada una excepción causa por ejemplo un SQLException puede ser causado por un IOException por fallo de comunicación con el servidor de base de datos.
Todas las excepciones en Java heredan de Throwable y entre los métodos que tiene esta clase está getStackTrace() que devuelve un array de StackTraceElement ordenado del último método llamado al primero. Con los métodos de la clase StackTraceElement obtenemos el nombre de la clase, el archivo, el nombre del método y la linea de código de esa llamada.
Con esta información podemos imprimir en la terminal un informe de excepción diferente del que genera el método printStackTrace(). En el ejemplo limitando el informe de la pila de llamadas a los tres últimos métodos del stacktrace.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Thread.currentThread().dumpStack();
try { throw new Exception(); } catch(Exception e) { e.printStackTrace(); }
try { throw new Exception(); } catch(Exception e) {
System.out.println(e.getClass().getName());
Arrays.asList(e.getStackTrace()).stream().limit(3).forEach(s ->
System.out.println(String.format("\tat %s%s.%s(%s)",
(s.getModuleName() == null) ? "" : String.format("%s/", s.getModuleName()),
s.getClassName(),
s.getMethodName(),
(s.isNativeMethod() ? "Native Method" : String.format("%s:%d", s.getFileName(), s.getLineNumber())))));
}
|
Throwable.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
$ jshell
| Welcome to JShell -- Version 9
| For an introduction type: /help intro
jshell> /edit
java.lang.Exception: Stack trace
at java.base/java.lang.Thread.dumpStack(Thread.java:1435)
at REPL.$JShell$11.do_it$($JShell$11.java:4)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209)
at jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116)
at jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119)
at jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:134)
at jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.commandLoop(ExecutionControlForwarder.java:225)
at jdk.jshell/jdk.jshell.execution.Util.forwardExecutionControl(Util.java:76)
at jdk.jshell/jdk.jshell.execution.Util.forwardExecutionControlAndIO(Util.java:137)
at jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.main(RemoteExecutionControl.java:70)
java.lang.Exception
at REPL.$JShell$12.do_it$($JShell$12.java:4)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209)
at jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116)
at jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119)
at jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:134)
at jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.commandLoop(ExecutionControlForwarder.java:225)
at jdk.jshell/jdk.jshell.execution.Util.forwardExecutionControl(Util.java:76)
at jdk.jshell/jdk.jshell.execution.Util.forwardExecutionControlAndIO(Util.java:137)
at jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.main(RemoteExecutionControl.java:70)
java.lang.Exception
at REPL.$JShell$13.do_it$($JShell$13.java:4)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
|
jshell.out
Si en una aplicación manejamos varios hilos con Thread.getAllStackTraces() obtenemos las pilas de llamadas de todos los hilos y con Thread.getStackTrace() el del hilo en concreto que con Thread.currentThread() sería el actual. Con el array de StackTraceElement obtenidos de los hilos podemos obtener un informe personalizado y la situación de cada uno, el método dumpStack() genera el stacktrace en la salida de error.
Entre las novedades de Java 9 está la clase StackWalker para procesar los elementos de la pila del thread actual usando streams y funciones lambda.