Tengo una clase de Java simple como se muestra a continuación:
public class Test {
private String s;
public String foo() {
try {
s = "dev";
return s;
}
finally {
s = "override variable s";
System.out.println("Entry in finally Block");
}
}
public static void main(String[] xyz) {
Test obj = new Test();
System.out.println(obj.foo());
}
}
Y el resultado de este código es este:
Entry in finally Block
dev
¿Por qué s
no se anula en el finally
bloque y controla la salida impresa?
java
try-finally
Dev
fuente
fuente
s
antes de cambiar su valor.finally
bloque , al contrario que en C # (que no se puede)Respuestas:
El
try
bloque se completa con la ejecución de lareturn
declaración y el valor des
en el momento enreturn
que se ejecuta la declaración es el valor devuelto por el método. El hecho de que lafinally
cláusula luego cambie el valor des
(después de que lareturn
declaración se complete) no (en ese punto) cambia el valor de retorno.Tenga en cuenta que lo anterior trata con cambios en el valor de
s
sí mismo en elfinally
bloque, no en el objeto al que haces
referencia. Sis
fue una referencia a un objeto mutable (queString
no lo es) y el contenido del objeto se modificó en elfinally
bloque, esos cambios se verían en el valor devuelto.Las reglas detalladas sobre cómo funciona todo esto se pueden encontrar en la Sección 14.20.2 de la Especificación del lenguaje Java . Tenga en cuenta que la ejecución de una
return
declaración cuenta como una terminación abrupta deltry
bloque (se aplica la sección que comienza " Si la ejecución del bloque try finaliza abruptamente por cualquier otro motivo R ... "). Ver Sección 14.17 del JLS para ver por qué unareturn
declaración es una terminación abrupta de un bloque.A modo de más detalles: si tanto el
try
bloque como elfinally
bloque de unatry-finally
declaración terminan abruptamente debido areturn
declaraciones, entonces se aplican las siguientes reglas del §14.20.2:El resultado es que la
return
declaración en elfinally
bloque determina el valor de retorno de toda latry-finally
declaración, y el valor devuelto deltry
bloque se descarta. Algo similar ocurre en unatry-catch-finally
declaración si eltry
bloque arroja una excepción, es atrapado por uncatch
bloque y tanto elcatch
bloque como elfinally
bloque tienenreturn
declaraciones.fuente
finally
bloque no cambia qué objeto se devuelve (elStringBuilder
) pero puede cambiar las partes internas del objeto. Elfinally
bloque se ejecuta antes de que el método realmente regrese (aunque lareturn
declaración haya finalizado), por lo que esos cambios ocurren antes de que el código de llamada vea el valor devuelto.StringBuilder
,List
,Set
, hasta la saciedad): si cambia el contenido en elfinally
bloque, a continuación, se observan los cambios en el código de llamada cuando el método finalmente salidas.Porque el valor de retorno se coloca en la pila antes de la llamada a finalmente.
fuente
=
no lo mutaría.finally
bloqueo de OP no afectó el valor de retorno. Creo que a lo que templatetypedef podría haber estado llegando (aunque esto no está claro) es que debido a que el valor devuelto es una referencia a un objeto inmutable, incluso cambiar el código en elfinally
bloque (aparte de usar otrareturn
declaración) no podría afectar el valor devuelto por el métodoSi miramos dentro de bytecode, notaremos que JDK ha realizado una optimización significativa, y el método foo () se ve así:
Y bytecode:
Java preservó la cadena "dev" de ser cambiada antes de regresar. De hecho, aquí no hay finalmente un bloqueo en absoluto.
fuente
Hay 2 cosas notables aquí:
fuente
finally
cláusula, eso se vería en el código de llamada. Sin embargo, si asignó un nuevo buffer de cadenas as
, entonces el comportamiento sería el mismo que ahora.Cambio un poco tu código para probar el punto de Ted.
Como puede ver en la salida, de
s
hecho, cambia pero después del regreso.Salida:
fuente
Técnicamente hablando, el
return
bloque en el try no se ignorará sifinally
se define un bloque, solo si ese bloque finalmente también incluye areturn
.Es una decisión de diseño dudosa que probablemente fue un error en retrospectiva (al igual que las referencias son anulables / mutables de forma predeterminada y, según algunos, excepciones marcadas). En muchos sentidos, este comportamiento es exactamente consistente con la comprensión coloquial de lo que
finally
significa: "no importa lo que ocurra de antemano en eltry
bloque, siempre ejecute este código". Por lo tanto, si devuelve verdadero de unfinally
bloque, el efecto general siempre debe serreturn s
, ¿no?En general, esto rara vez es una buena expresión idiomática, y debe usar
finally
bloques generosamente para limpiar / cerrar recursos, pero rara vez devuelve un valor de ellos.fuente
Pruebe esto: si desea imprimir el valor de anulación de s.
fuente