2012-06-25 25 views
14
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?¿Comportamiento extraño finalmente?

la salida del programa es

finally value of q is hello 
hii 
+1

Le recomendamos leer esto: http://stackoverflow.com/questions/65035/in-java-does-return-trump-finally –

+2

El valor de la variable local 'q' se ha cambiado a" hola ", sí. Pero lo que devolviste fue "hii". – maksimov

+0

Mire mi nueva respuesta ... intentemos refinar este concepto más ... – Ahmad

Respuesta

27

Eso es porque regresó un valor que se evaluó a partir q antes de cambiar el valor de q en el bloque finally. Usted devolvió q, que evaluó su valor; luego cambió q en el bloque finally, que no afectó el valor cargado; luego se completa el retorno, utilizando el valor evaluado.

No escriba códigos engañosos 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.

+0

pero el valor se devuelve después de que se ejecuta el bloque finally ... entonces – Harinder

+1

@Dennis El valor a devolver se cargó antes de que se ejecutara el bloque finally. Ya dije eso. – EJP

+1

@Dennis sí, pero en 'finally' ya está _past_ la instrucción' return', no inmediatamente antes. – maksimov

0

[Editado después del comentario de EJP, mi primera respuesta no respondió la pregunta y también fue incorrecta.]
Ahora mi respuesta debe 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.

Tenga una mirada en JLS 14.20.2 Execution of try-catch-finally

Una sentencia try con un bloque finally se ejecuta por primera ejecución del bloque try. A continuación, hay una opción:

Si la ejecución del bloque try se completa con normalidad, a continuación, se ejecuta el bloque finally, y luego hay una opción:
Si el bloque finally se completa con normalidad, entonces la instrucción try finaliza con normalidad.
[...]

y JLS 14.17 The return Statement

Una instrucción de retorno con una expresión intentos de 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 declaración de devolución primero evalúa la Expresión. Si la evaluación de Expression se completa abruptamente por algún motivo, la declaración de devolución se completa abruptamente por ese motivo. Si la evaluación de la expresión completa con normalidad, produciendo un valor V, entonces la instrucción de retorno termina abruptamente, por la razón de una vuelta con el valor V

Y:

Las descripciones anteriores dicen que "los intentos de transferir controlar "en lugar de simplemente" transfiere el control "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 instrucciones try será ejecutada, en orden, más interna para más externo, antes de que el control se transfiera 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 declaración de devolución.

+1

¿Cómo responde esto exactamente la pregunta? – EJP

+0

Aún no responde la pregunta. 'q' no * es * devuelto. Se devuelve el * valor de q cuando se ejecutó la declaración de devolución *. – EJP

4

return vuelve valor no referencia. Cuando se ejecuta return q; en catch, el valor actual de q se almacena en caché por el método como resultado. Entonces, incluso si en el bloque finally reasignará q con un nuevo valor, no afectará el valor ya almacenado en caché por el método.

Si desea actualizar el valor que debe ser devuelto tendrá que usar otro return en su bloque finally como

} finally { 
    q = "hello"; 
    System.out.println("finally value of q is " + q); 

    return q;//here you set other value in return 
} 

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

} 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); 
} 
+0

'Valor (objeto) + de + referencia, no referencia exacta', 'objeto de lectura/almacenamiento de referencia' y 'objeto almacenado a cambio' no tienen sentido. No es una respuesta. Probablemente hay una respuesta aquí luchando por salir, pero la terminología es tan confusa que no se transmite ningún significado. – EJP

2

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.

0

Pruebe usar StringBuffer en lugar de String y verá el cambio .... parece que la declaración de devolución bloquea el objeto que debe devolverse y no la referencia. También puede probar a comprobar al imprimir el código hash de:

  • objeto de ser devuelto desde go()
  • objeto en fin
  • objeto que está siendo impreso 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); 
         } 
        } 
    
+0

'Bloquea el objeto que se va a devolver' no tiene sentido. No es una respuesta. – EJP

-1

Bueno, es lo que he encontrado como sigue,

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

Permite verificarlo siguiendo los siguientes 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 programa es

por último valor de q es hola

HII

y si tomamos StringBuffer en lugar de cuerdas 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;*/ 

     } 

    } 
} 

El comesout salida sea,

por último valor de q es hola

hola

y, finalmente, si tomamos int en lugar de la cadena 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 valor de q es 2

       **Ananlysis** 

1.In primer caso, devolver dirección de copiado de cadena en la variable a, entonces excecution va a finalmente donde se cambia la cadena. Pero dado que en el caso de Strings, no podemos manipular cualquier String, se construye una nueva String. Así que en la variable una dirección de cadena original se guarda, que se imprime.

2.In segundo caso, remite copia de StringBuffer en la variable un, y finalmente se manipula este objeto StringBuffer, en lugar de crear uno nuevo. por lo que el valor que se almacena en la variable un también recibe el manipulado, que ha visto en el estado de impresión.

3.En el tercer caso, el valor de int se copia en la variable a, antes de ejecutar por fin. 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.

+0

bastante bien ... no leyó su respuesta ... – Ahmad

+0

Sus dos primeros ejemplos son irrelevantes. El primero es simplemente código incorrecto, ya que String.repace() no muta el valor, sino que devuelve uno nuevo que está descartando. El segundo cambia el valor, pero como eso no es lo que está haciendo el OP, no veo el punto. El tercero simplemente reitera lo que el OP está preguntando y repite la respuesta que ya se le dio. – EJP

0

Lo que finalmente bloquear?

-By definition from Java "El bloque finally siempre se ejecuta cuando se cierra el bloque try. Esto asegura que el bloque finally se ejecute incluso si se produce una excepción inesperada."

Así, se imprime "Finalmente valor de q es hola" tan pronto como existe el bloque try 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 corte y despertando el código.