Extraño finalmente el comportamiento?

public class Test2 { public static void main(String[] args) { Test2 obj=new Test2(); String a=obj.go(); System.out.print(a); } public String go() { String q="hii"; try { return q; } finally { q="hello"; System.out.println("finally value of q is "+q); } } 

¿Por qué esta impresión hii después de regresar de la función go() , el valor ha cambiado a “hola” en el bloque finally?

la salida del progtwig es

 finally value of q is hello hii 

Esto se debe a que devolvió un valor que se evaluó desde q antes de cambiar el valor de q en el bloque finally. Usted devolvió q , que evaluó su valor; luego cambiaste q en el bloque finally , que no afectó el valor cargado; luego se completa el retorno, utilizando el valor evaluado.

No escriba código complicado como este. Si confunde al tipo que lo escribió, imagínese los problemas que causará al siguiente tipo, unos años más cuando esté en otro lado.

return devuelve valor no referencia. Cuando return q; se ejecuta en el valor actual de catch de referencia q se almacena en caché por método como resultado. Así que incluso si finally bloquear finally reasignas q con un nuevo valor, no afecta el valor ya almacenado en caché por el método.

Si desea actualizar el valor que debe devolverse, tendrá que usar otra return en su bloque final como

 } finally { q = "hello"; System.out.println("finally value of q is " + q); return q;//here you set other value in return } 

Otra forma de afectar el valor devuelto es cambiando el estado del objeto en caché. Por ejemplo, si q fuera una List , podríamos agregarle un nuevo elemento (pero observe que cambiar el estado no es lo mismo que reasignar una nueva instancia, al igual que podemos cambiar el estado de final variable final , pero no podemos reasignarla).

 } finally { q.add(new Element); //this will place new element (update) in List //object stored by return because it is same object from q reference System.out.println("finally value of q is " + q); } 

Finalmente se ejecuta después de la devolución pero antes de que el método realmente regrese a la persona que llama. Esto es análogo a tirar. Ocurre después del lanzamiento y antes de salir del bloque. El valor de retorno ya está establecido en algún registro leyendo la variable q. Si q era mutable, podrías mutarlo finalmente y verías ese cambio en la persona que llama. ¿Por qué funciona de esta manera? Por un lado, probablemente sea el menos complicado de implementar. Dos, te da la máxima flexibilidad. Puede anular el valor de retorno finalmente con un retorno explícito. Preservarlo de forma predeterminada le permite elegir cualquiera de los comportamientos.

[ Editado después del comentario de EJP, mi primera respuesta no respondió la pregunta y también fue incorrecta. ]
Ahora mi respuesta debería ser correcta explicando que a medida que el bloque try y el bloque finally finalizan normalmente q se devuelve. Y la razón por la que se devuelve el valor “hii” se explica en la respuesta de los EJP. Todavía estoy buscando una explicación en el JLS.

Eche un vistazo a JLS 14.20.2 Ejecución de try-catch-finally

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 normalmente, 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 normalmente.
[…]

y JLS 14.17 Declaración de devolución

Una statement de devolución con una expresión intenta transferir el control al invocador del método que la contiene; el valor de Expression se convierte en el valor de la invocación del método. Más precisamente, la ejecución de dicha statement de devolución primero evalúa la Expresión. Si la evaluación de Expression se completa abruptamente por algún motivo, la statement de devolución se completa abruptamente por ese motivo. Si la evaluación de la Expresión se completa normalmente, produciendo un valor V, la statement de retorno se completa abruptamente, siendo el motivo un retorno con valor V

Y:

Las descripciones anteriores dicen “bashs de transferir control” en lugar de solo “control de transferencias” porque si hay alguna instrucción try (§14.20) dentro del método o constructor cuyos bloques try contengan la instrucción return, entonces cualquier cláusula finally de esas declaraciones try ser ejecutado, en orden, más interno a más externo, antes de que el control sea transferido al invocador del método o constructor. La terminación abrupta de una cláusula finally puede interrumpir la transferencia de control iniciada por una statement de devolución.

Intente utilizar StringBuffer en lugar de String y verá el cambio … parece que la statement de devolución bloquea el objeto que se debe devolver y no la referencia. También puede intentar verificar esto imprimiendo el código hash de:

  • objeto que se devuelve desde go ()
  • objeto en fin
  • objeto que se imprime desde main ()

    public static void main (String [] args) {

      Test obj=new Test(); StringBuffer a=obj.go(); System.out.print(a); } public StringBuffer go() { StringBuffer q=new StringBuffer("hii"); try { return q; } finally { q=q.append("hello"); System.out.println("finally value of q is "+q); } } 

¿Cuál es finalmente el bloque?

“Por definición desde 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 “.

Entonces, imprime “finalmente el valor de q es hola” tan pronto como existe el bloque de prueba y va a la línea System.out.print (a); e imprime el valor devuelto por el método go ().

Si tiene depuradores como netbeans o eclipse, puede analizarse manteniendo el punto de quiebre y despertando el código.

Bueno, lo que encontré es lo siguiente,

Return realmente devuelve un valor y se copia a String a=obj.go(); , antes de la ejecución va a Finalmente .

Vamos a verificarlo siguiendo los experimentos.

 public class Test2 { public static void main(String[] args) { Test2 obj=new Test2(); String a=obj.go(); System.out.print(a); } public String go() { String q="hii"; try { return q; } finally { q="hello"; System.out.println("finally value of q is "+q); } } 

la salida del progtwig es

finalmente el valor de q es hola

Hola yo

y si tomamos StringBuffer en lugar de String de la siguiente manera,

 public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Test2 obj=new Test2(); StringBuffer a=obj.go(); System.out.print(a); } public StringBuffer go(){ StringBuffer q=new StringBuffer("hii"); try{ return q; } finally{ q.replace(0, q.length(), "hello"); System.out.println("finally value of q is "+q); /*return q1;*/ } } } 

La salida sale a ser,

finalmente el valor de q es hola

Hola

y finalmente si tomamos int en lugar de String de la siguiente manera,

 public class Test2 { public static void main(String[] args) { // TODO Auto-generated method stub Test2 obj=new Test2(); int a=obj.go(); System.out.print(a); } public int go(){ int q=1; try{ return q; } finally{ q=2; System.out.println("finally value of q is "+q); /*return q1;*/ } } } 

la salida es

finalmente el valor de q es 2

1

  **Ananlysis** 

1.En el primer caso, devuelva la dirección copiada de String en la variable a , luego la ejecución pasa a Finally donde se cambia String. Pero dado que en el caso de Strings, no podemos manipular cualquier String, se construye una nueva String. Entonces, en la variable, se guarda una dirección de la cadena original, que se imprime.

2.En el segundo caso, devuelva la dirección copiada de StringBuffer en la variable a , y finalmente este objeto StringBuffer es manipulado, en lugar de crear uno nuevo. entonces el valor que se almacenó en la variable a también se manipula, eso se ve en la statement impresa.

3.En el tercer caso, el valor de int se copia en la variable a , antes de que la ejecución llegue finalmente. y así a obtiene el valor de 1. y luego, finalmente, cambiamos el valor de q, que de todos modos no cambia el valor de a .