16
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);
        }
    }

hii関数から戻った後にこの出力が行われるのはなぜですかgo()。finallyブロックの値が「hello」に変更されていますか?

プログラムの出力は

finally value of q is hello
hii
4

7 に答える 7

27

これは、finally ブロックqの値を変更する前に評価された値を返したためです。その値を評価したqが返されました。次に、ブロックqを変更しましたが、ロードされた値には影響しませんでした。次に、評価された値を使用して、リターンが完了しました。qfinally

このようなトリッキーなコードを書かないでください。それを書いた人を混乱させるなら、数年後、あなたが別の場所にいる次の人にどんな問題を引き起こすか想像してみてください。

于 2012-06-25T10:07:11.890 に答える
4

return変数ではなく値を返します。変数の値がセクションでreturn q;実行されると、メソッドの結果としてキャッシュされます。メソッドがの現在の値を覚えているようなものですが、それが返される前にいくつかのことを最終決定することもできます。catchqq

したがって、finallyブロック内で新しい値を割り当ててもq、メソッドによってキャッシュされた値は変更されません。

戻り値を更新したい場合は、別のreturninfinallyブロックを使用する必要があります

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

    return q; // <--- this will set new value which should be returned
}

返された「値」に影響を与える他の方法は、その状態を変更することです。

たとえば、最終ブロックで新しい要素を追加できqた場合List

} 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);
}
于 2012-06-25T10:21:31.647 に答える
2

最後に、戻り後、メソッドが実際に呼び出し元に戻る前に実行されます。これは投げることに似ています。スローの後、ブロックを終了する前に発生します。戻り値は、変数 q を読み取ることによって、すでにいくつかのレジスターに設定されています。q が変更可能である場合、finally でそれを変更すると、呼び出し元でその変更が表示されます。なぜこのように機能するのですか?1 つには、おそらく実装が最も簡単です。2 つ目は、最大限の柔軟性を提供することです。明示的な return を使用して、finally の戻り値をオーバーライドできます。デフォルトで保持すると、どちらかの動作を選択できます。

于 2012-06-25T10:22:58.603 に答える
0

Stringの代わりにStringBufferを使用してみると、変更が表示されます.... returnステートメントは、参照ではなく、返されるオブジェクトをブロックしているようです。次のハッシュコードを出力して、これを確認することもできます。

  • go()から返されるオブジェクト
  • 最後にオブジェクト
  • 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);
            }
        }
    
于 2012-06-25T11:15:28.907 に答える
0

最終的にブロックとは何ですか?

-Javaの定義によると、 「finallyブロックは、tryブロックが終了すると常に実行されます。これにより、予期しない例外が発生した場合でも、finallyブロックが確実に実行されます。」

したがって、tryブロックが存在し、System.out.print(a)の行に移動するとすぐに、「最終的にqの値はhelloです」と出力されます。そして、メソッドgo()によって返された値を出力します。

netbeansやeclipseのようなデバッガーを使用している場合は、ブレークポイントを維持し、コードを目覚めさせることで分析できます。

于 2013-01-12T19:51:33.193 に答える
0

[ EJP からのコメントを編集した後、私の最初の回答は質問に答えず、また間違っていました。]
これで、try ブロックと finally ブロックが正常に完了すると q が返されることを説明する私の答えは正しいはずです。また、値「hii」が返される理由は、EJPs answer で説明されています。私はまだJLSで説明を探しています。

JLS 14.20.2 Execution of try-catch-finally をご覧ください

finally ブロックを含む try ステートメントは、最初に try ブロックを実行することによって実行されます。次に、選択肢があります。

try ブロックの実行が正常に完了した場合、finally ブロックが実行されます。その後、選択肢があります
。finally ブロックが正常に完了した場合、try ステートメントは正常に完了します。
[...]

およびJLS 14.17 return ステートメント

Expression を含む return ステートメントは、それを含むメソッドの呼び出し元に制御を移そうとします。Expression の値がメソッド呼び出しの値になります。より正確には、このような return ステートメントを実行すると、最初に Expression が評価されます。Expression の評価が何らかの理由で突然完了した場合、return ステートメントはその理由で突然完了します。Expression の評価が正常に完了して値 V が生成された場合、return ステートメントは突然完了します。その理由は値 V での戻りです。

と:

前の説明では、単に「制御を移す」というよりも「制御を移そうとする試み」と言っています。なぜなら、try ブロックに return ステートメントが含まれるメソッドまたはコンストラクター内に try ステートメント (§14.20) がある場合、それらの try ステートメントの finally 句は、制御がメソッドまたはコンストラクターの呼び出し元に転送される前に、最も内側から最も外側の順に実行されます。finally 句が突然完了すると、return ステートメントによって開始された制御の転送が中断される可能性があります。

于 2012-06-25T10:18:26.213 に答える
-1

さて、私が見つけたのは次のとおりです、

Returnは実際に値を返し、 String a=obj.go();実行がFinallyに進む前に、その値がにコピーされます。

以下の実験で検証してみましょう。

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

プログラムの出力は

最後にqの値はこんにちは

hii

次のようにStringの代わりにStringBufferを使用すると、

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;*/

        }

    }
}

出力は次のようになります、

最後にqの値はこんにちは

こんにちは

最後に、次のようにStringの代わりにintを使用すると、

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;*/

        }

    }
}

出力は

最後にqの値は2です

1

                              **Ananlysis**

1.最初のケースでは、変数aに文字列のコピーされたアドレスを返し、実行は最後に文字列が変更された場所に移動します。ただし、文字列の場合、文字列を操作できないため、新しい文字列が作成されます。したがって、変数には元の文字列アドレスが保存され、印刷されます。

2. 2番目のケースでは、StringBufferのコピーされたアドレスを変数aに返し、最後にこのStringBufferオブジェクトを操作して、新しいオブジェクトを作成します。したがって、変数aに格納された値も操作されます。これは、printステートメントで確認できます。

3. 3番目のケースでは、実行がfinallyに進む前に、intの値が変数aにコピーされます。したがって、aは1の値を取得し、最後にqの値を変更しましたが、とにかくaの値は変更されません。

于 2012-06-26T06:42:04.423 に答える