java - excepciones - Finalmente a veces me confundes
try catch java exception e (8)
¿Qué pasa si algo en el bloque catch arrojará Exception? out.close no se ejecutará. También puede usar " probar con recursos " para asegurarse de que todos los recursos se cierren después de su uso. Prueba este ejemplo:
public static void withFinnaly() {
try {
throwException();
System.out.println("This won''t execute");
} catch (Exception e) {
System.out.println("Exception is caught");
throwException();
} finally {
System.out.println("Finally is always executed," +
" even if method in catch block throwed Exception");
}
}
public static void withOutFinnaly() {
try {
throwException();
System.out.println("This won''t execute");
} catch (Exception e) {
System.out.println("Exception is caught");
throwException();
}
System.out.println("Looks like we''ve lost this... " +
"This wont execute");
}
public static void throwException() throws RuntimeException {
throw new RuntimeException();
}
Esta pregunta ya tiene una respuesta aquí:
Hoy en la universidad hablamos un poco acerca de try
, catch
y finally
. Me confundí acerca de estos dos ejemplos:
PrintWriter out = null;
try {
out = new PrintWriter(...); // We open file here
} catch (Exception e) {
e.printStackTrace();
} finally { // And we close it here
out.close();
}
Cuál es la diferencia entre cerrar el archivo finally
y si lo hicimos de esta manera:
PrintWriter out = null;
try {
out = new PrintWriter(...); // We open file here
} catch (Exception e) {
e.printStackTrace();
}
out.close();
Esta pieza de código después de captura siempre se ejecutará.
¿Me puede dar algunos buenos ejemplos sobre las diferencias entre cuando usamos finally
y cuando colocamos el código después de la captura? Sé que finalmente siempre se ejecutará, pero el programa también seguirá ejecutándose después del bloqueo catch.
Aunque no son respuestas completas, estos dos ejemplos de try-finally
(mis) use pueden ser esclarecedores:
public class JavaApplication3
{
static int foo()
{
try
{
return 6;
}
finally
{
return 4;
}
}
public static void main(String[] args)
{
System.out.println("foo: " + foo());
}
}
public class JavaApplication3
{
static int foo()
{
try
{
throw new Exception();
}
finally
{
return 4;
}
}
public static void main(String[] args)
{
System.out.println("foo: " + foo());
}
}
Ambos programas producen 4 .
La razón para esto se puede encontrar en el Capítulo 14.20.2 de JLS
Una instrucción try con un bloque finally se ejecuta ejecutando primero el bloque try. Entonces hay una opción:
• Si la ejecución del bloque try se completa abruptamente debido a un lanzamiento de un valor V, entonces hay una opción:
[...]
- Si el tipo de V en tiempo de ejecución no es compatible con la asignación con una clase de excepción atrapable de cualquier cláusula catch de la instrucción try, entonces se ejecuta el bloque finally. Entonces hay una opción:
[...]
> Si el bloque finally se completa bruscamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S (y el lanzamiento del valor V se descarta y se olvida).• Si la ejecución del bloque try se completa abruptamente por cualquier otra razón R , entonces se ejecuta el bloque finally, y luego hay una opción:
- Si el bloque finally se completa normalmente, la instrucción try se completa abruptamente por el motivo R.
- Si el bloque finally se completa abruptamente por la razón S, entonces la instrucción try se completa abruptamente por la razón S (y la razón R se descarta).
Editando el mío
Considere que un return
es una forma abrupta de completar una try
o finally
bloquear.
El caso de uso normal para finalmente es cuando no desea atrapar la excepción en el mismo método.
en ese caso, utilizas un try con finally block sin tener un catch. De esta forma, puede asegurarse de que sus recursos estén cerrados sin tener que atrapar la excepción en el método en sí.
En Java, el código fuente:
void foo()
{
try {
if (W())
return;
}
catch (FooException ex) {
if (X())
throw;
}
finally {
Y();
}
Z();
}
será convertido por el compilador en:
void foo()
{
try {
if (W()) {
Y();
return;
}
}
catch (FooException ex) {
if (X()) {
Y();
throw;
}
}
catch {
Y();
throw;
}
Y();
Z();
}
El efecto es hacer que el código dentro del bloque Finally se duplique en todos los lugares donde el control puede dejar el método. Cualquier bloque try
que tenga un controlador final, pero no catch-all, es equivalente a uno con un controlador catch-all que inmediatamente lanza (en el que el procesador podría insertar una copia del código finally
antes del controlador catch-all.
Si se produce un error no detectado dentro del bloque try
, o incluso si ocurre un error dentro de su bloque catch
, la ''pieza de código'' después del catch no se ejecutará, pero el bloque finally
hará.
finally
siempre será ejecutado.
De la documentación de Java:
El bloque finally siempre se ejecuta cuando sale el bloque try. Esto asegura que el bloque finally se ejecuta incluso si ocurre una excepción inesperada. Pero finalmente es útil para algo más que el manejo de excepciones: permite que el programador evite que el código de limpieza sea anulado accidentalmente por un retorno, continuación o interrupción. Poner el código de limpieza en un bloque finally es siempre una buena práctica, incluso cuando no se prevén excepciones.
Su segundo ejemplo puede arrojar una NullPointerException
no deseada si se produce una excepción en el contructor de PrintWriter
.
Otra posibilidad es que out.close();
arrojará un error, que no se detecta.
Si mueve su código a un bloque finally
, siempre se ejecutará, independientemente de si el bloque try tiene éxito o no. Esto es especialmente útil si su try
-block genera una excepción que no se detecta. En su segundo ejemplo, esto provocará que out.close()
no se ejecute, mientras que con un bloque finally, se ejecutará incluso si el bloque try
arroja un error no capturado.
Todavía marcaría la diferencia si el código arroja un Error
. Esto no está incluido en el código y, por lo tanto, no se try/catch/finally
ninguna parte después de try/catch/finally
. Si es parte de finally
, se ejecutará incluso para Error
.
En segundo lugar, si por alguna razón e.printStackTrace()
arroja una excepción (aunque sería muy raro), ocurrirá lo mismo, finally
se ejecutará.
En general, finally
es una forma muy segura de liberar recursos sin importar lo que suceda. Aún más seguro es probar con recursos admitidos desde Java 7, ya que podría gestionar fácilmente múltiples excepciones lanzadas durante operaciones cercanas. En este ejemplo, se vería así:
try (PrintWriter out = new PrintWriter(...)) {
// do whatever with out
}
catch (Exception e) {
e.print... (whatever)
}
// no need to do anything else, close is invoked automatically by try block
EDITAR: También tenga en cuenta que su código no es realmente correcto (no importa qué versión). Si el constructor PrintWriter
lanza una excepción, la línea out.close()
fallará en NullPointerException
.
Un ejemplo más pequeño:
PrintWriter out = null;
try {
out = new PrintWriter();
out.print(data);
} finally {
out.close();
}
Aquí, no detectamos ninguna excepción (eso es manejado por la persona que llama), pero sí queremos close
el escritor si
- operar normalmente a través del bloque
try
, o - salir a través de una excepción.
En ambos casos, el código en finally
se ejecuta como parte de abandonar el bloque.
Aquí, tomamos un subconjunto de excepciones:
PrintWriter out = null;
try {
out = new PrintWriter();
out.print(data);
} catch (IOException e) {
log(e);
} finally {
out.close();
}
do_something_else();
Aquí, hay tres caminos posibles:
- la ejecución normal de la parte
try
, seguida porfinally
, y luegodo_something_else()
, - la parte
try
arrojaIOException
, que se captura y registra, luegofinally
ejecuta el bloquefinally
y luegodo_something_else()
, o - la parte de
try
arroja un throwable diferente, que no se captura, perofinally
se ejecuta el bloque, luego el código salta al siguientetry
encierre, posiblemente en la persona que llama.
Tenga cuidado al escribir su bloque finally
: no está dentro del try
, por lo que cualquier excepción preevaluará cualquier excepción que esté en progreso. El consejo corto es tratar de evitar cosas que puedan arrojarse desde adentro o catch
.