これはインタビューの質問でした:
public class Demo {
public static void main(String[] args) {
System.out.println(foo());
}
static String foo() {
try {
return "try ...";
} catch (Exception e) {
return "catch ...";
} finally {
return "finally ..."; //got as result
}
}
}
私の質問は、コンパイル時のエラーがない理由です。 finally
ブロックにreturnステートメントがある場合、finally
およびtry
ブロックの代わりにcatch
から戻るようにバインドされています。このコードを-Xlint
オプションを使用してコンパイルしようとしたところ、警告が表示されました。
warning: [finally] finally clause cannot complete normally
Java言語仕様で許可されているため、コンパイルエラーは発生しません。ただし、return
ブロックにfinally
ステートメントを含めることは通常悪い考えであるため、警告メッセージが表示されます。
あなたの例で何が起こるかは次のとおりです。 return
ブロックのtry
ステートメントが実行されます。ただし、finally
ブロックは常に実行する必要があるため、catch
ブロックの終了後に実行されます。そこで発生するreturn
ステートメントは、前のreturn
ステートメントの結果を上書きするため、メソッドは2番目の結果を返します。
同様に、finally
ブロックは通常、例外をスローしません。そのため、finally
ブロックは正常に、つまりreturn
なしで、または例外をスローせずに完了する必要があると警告が示しています。
これはJava言語仕様で説明されています:
finally
句が突然完了すると、return
ステートメントによって開始された制御の移行が中断される可能性があります。
try
ブロックの実行が正常に完了した場合、finally
ブロックが実行され、次に選択肢があります。
finally
ブロックが正常に完了すると、try
ステートメントは正常に完了します。finally
ブロックが理由Sで突然完了した場合、try
ステートメントは理由Sで突然完了します。
try
ブロックの実行が他の理由Rで突然完了した場合、finally
ブロックが実行され、次に選択肢があります。
finally
ブロックが正常に完了した場合、try
ステートメントは理由Rのために突然完了します。- 理由Sのために
finally
ブロックが突然完了すると、理由Sのためにtry
ステートメントが突然完了する(そして理由Rが破棄される)。
実際にはreturn
ステートメントの1つだけが実際にコントロールを呼び出し元のコードに返すため、コンパイル時のエラーは発生しません。
@Hoopjeで説明したように、return
またはtry
内のcatch
が最初に実行され、それぞれのreturnステートメントも実行されます。ただし、コントロールを呼び出しコードに戻す直前に、finally
ブロックを実行します。現在、これはblock
もreturn
sなので、この戻り値は以前の戻り値をオーバーライドします。
すばらしい質問..私の知識によれば、try and catchブロックのreturnステートメントは、finallyブロックをコードに追加した場合、finallyに転送されます。それがその働きです。
したがって、この場合はすべてのコード行が実行中であり、デバッグを試すことができます。コードの下で試した3つのブロックすべて。
public class Main {
public static void main(String[] args) {
System.out.println(foo());
}
static String foo() {
try {
throw new Exception();
} catch (Exception e) {
return "catch ...";
} finally {
return "finally ..."; //got as result
}
}
}
以下のリンクからアイデアを得ることができます。 複数の戻り値:最終戻り値を設定するものはどれですか?
それは基本的にこれと同じです:
public boolean someMethod(){
if(1 == 1){
return true;
}
return false;
}
警告は表示されますが、コンパイルエラーは発生しません。 no returnステートメントが実行される可能性がある場合にのみ、コンパイラーはエラーを出します。
Try、catch、finallyブロックにはreturnステートメントが1つしかないため、コードは正常に機能します。到達不能なreturnステートメントがあることを伝えるために、try、catch、またはfinallyブロックのいずれかで2つのreturnステートメントを書き込もうとすると、コンパイルエラーが発生します。
(短い回答の場合-回答の太字と斜体の部分をお読みください)
Java 8 docsによる実行フロー。詳細を提供します。以下に基づいてreturnステートメントの実行を推測できます。
Finallyブロックを含むtryステートメントは、最初にtryブロックを実行することによって実行されます。
次に選択肢があります:
•tryブロックの実行が正常に完了すると、finallyブロックが実行され、次に選択肢があります。
– finallyブロックが正常に完了すると、tryステートメントが正常に完了します。
– finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します。
•値Vがスローされたためにtryブロックの実行が突然完了した場合は、選択肢があります。
– Vの実行時の型が、tryステートメントのいずれかのcatch句のキャッチ可能な例外クラスと割り当て互換である場合、最初(左端)のそのようなcatch句が選択されます。値Vが選択されたcatch節のパラメーターに割り当てられ、そのcatch節のBlockが実行されます。
次に選択肢があります:
› catchブロックが正常に完了すると、finallyブロックが実行されます。次に選択肢があります:
"finallyブロックが正常に完了すると、tryステートメントが正常に完了します。
"finallyブロックが何らかの理由で突然完了した場合、tryステートメントは同じ理由で突然完了します。
›理由Rのためにcatchブロックが突然完了すると、finallyブロックが実行されます。次に選択肢があります:
"finallyブロックが正常に完了すると、理由Rのためにtryステートメントが突然完了します。
"finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(理由Rは破棄されます)。
– Vのランタイムタイプが、tryステートメントのいずれかのcatch句のキャッチ可能な例外クラスとの割り当て互換性がない場合、finallyブロックが実行されます。
次に選択肢があります:
› finallyブロックが正常に完了すると、値Vがスローされたため、tryステートメントが突然完了します。
› finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(値Vのスローは破棄され、忘れられます)。
•tryブロックの実行が他の理由Rで突然完了した場合、finallyブロックが実行され、次に選択肢があります。
– finallyブロックが正常に完了すると、理由Rのためにtryステートメントが突然完了します。
– finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(理由Rは破棄されます)。
説明はこのリンクで明確です- javaDoc
これを実行してみてください:
1、2、3が出力され、ゼロ除算例外がスローされます。
public class Demo {
public static void main(String[] args) {
System.out.println(foo());
}
public static String print(int a){
System.out.println(a);
return String.valueOf(a/0);
}
static String foo() {
try {
return print(1);
} catch (Exception e) {
return print(2);
} finally {
return print(3);
}
}
}