Try/catch/finallyブロックに戻ることを伴う頭痛の種を知っています-tryまたはcatchブロックの戻りが実行されるべきであっても、finallyの戻りが常にメソッドの戻りである場合。
ただし、System.exit()にも同じことが当てはまりますか?たとえば、tryブロックがある場合:
try {
//Code
System.exit(0)
}
catch (Exception ex) {
//Log the exception
}
finally {
System.exit(1)
}
例外がない場合、どのSystem.exit()が呼び出されますか?出口がreturnステートメントである場合、System.exit(1)行が常に(?)呼び出されます。ただし、exitがreturnと異なる動作をするかどうかはわかりません。
コードは極端な場合であり、不可能ではないにしても、再現するのは非常に難しいため、単体テストを書くことはできません。数分無料の時間があれば、今日後で実験を実行しようとしますが、とにかく興味があり、おそらくSOまたは、実験を実行できない場合。
いいえ。System.exit(0)
は返されず、finallyブロックは実行されません。
System.exit(int)
はSecurityException
をスローできます。その場合、finallyブロックwillが実行されます。また、同じプリンシパルが同じコードベースから同じメソッドを呼び出しているため、2番目の呼び出しから別のSecurityException
がスローされる可能性があります。
次に、2番目のケースの例を示します。
import Java.security.Permission;
public class Main
{
public static void main(String... argv)
throws Exception
{
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm)
{
/* Allow everything else. */
}
@Override
public void checkExit(int status)
{
/* Don't allow exit with any status code. */
throw new SecurityException();
}
});
System.err.println("I'm dying!");
try {
System.exit(0);
} finally {
System.err.println("I'm not dead yet!");
System.exit(1);
}
}
}
catch
を含む簡単なテストでも、system.exit(0)
がセキュリティ例外をスローしない場合、最後に実行されたステートメントになることがわかります(catch
およびfinally
はまったく実行されます)。
system.exit(0)
がセキュリティ例外をスローする場合、catch
およびfinally
ステートメントが実行されます。 catch
とfinally
の両方にsystem.exit()
ステートメントが含まれる場合、これらのsystem.exit()
ステートメントの前のステートメントのみが実行されます。
上記の両方のケースで、try
コードが別のメソッドによって呼び出されたメソッドに属している場合、呼び出されたメソッドは戻りません。
詳細 ここ (個人ブログ)。
他の回答では、System.exit
がcatch
をスローせずにJVMを終了する場合にfinally
およびSecurityException
ブロックが実行されない方法について説明しましたが、リソースの「try-with-resources」ブロック:リソースは閉じられていますか?
JLS、セクション14.20.3.2 によると:
変換の効果は、リソース仕様をtryステートメントの「内側」に置くことです。これにより、拡張されたtry-with-resourcesステートメントのcatch節が、リソースの自動初期化またはクローズによる例外をキャッチできます。
さらに、finallyキーワードの意図に沿って、finallyブロックが実行されるまでにすべてのリソースが閉じられます(または閉じようとします)。
つまり、リソースはclose
またはcatch
ブロックが実行される前にfinally
dになります。 close
とcatch
が実行されなくても、それらが何らかの形でfinally
dである場合はどうなりますか?
「try-with-resources」ステートメントのリソースも閉じられていないことを示すコードを次に示します。
super.close
を呼び出す前にステートメントを出力するBufferedReader
の単純なサブクラスを使用します。
class TestBufferedReader extends BufferedReader {
public TestBufferedReader(Reader r) {
super(r);
}
@Override
public void close() throws IOException {
System.out.println("close!");
super.close();
}
}
次に、try-with-resourcesステートメントでSystem.exit
を呼び出すテストケースを設定しました。
public static void main(String[] args)
{
try (BufferedReader reader = new TestBufferedReader(new InputStreamReader(System.in)))
{
System.out.println("In try");
System.exit(0);
}
catch (Exception e)
{
System.out.println("Exception of type " + e.getClass().getName() + " caught: " + e.getMessage());
}
finally
{
System.out.println("finally!");
}
}
出力:
試しに
したがって、catch
およびfinally
ブロックが実行されないだけでなく、System.exit
が成功した場合、 "try-with-resources"ステートメントはそのリソースをclose
する機会を得ません。 。
tryブロックがthrowable(exceptionまたはerror)をスローしても、最終的にブロックは実行されます。
system.exit()メソッドを呼び出すときは、finallyブロックが実行されない場合のみです。
try{
System.out.println("I am in try block");
System.exit(1);
} catch(Exception ex){
ex.printStackTrace();
} finally {
System.out.println("I am in finally block!!!");
}
Finallyブロックは実行されません。プログラムはSystem.exit()ステートメントの後に終了します。
この動作に問題があり、_System.exit
_呼び出しを細かく制御する必要がある場合、できることは、独自のロジックでSystem.exit機能をラップすることだけです。そうすれば、finallyブロックを実行し、出口フローの一部としてリソースを閉じることができます。
私がやろうとしているのは、_System.exit
_呼び出しと機能を独自の静的メソッドでラップすることです。 exit
の実装では、Throwable
またはError
のカスタムサブクラスをスローし、その例外を処理するために_Thread.setDefaultUncaughtExceptionHandler
_でカスタムUncaught例外ハンドラを実装します。したがって、私のコードは次のようになります。
_//in initialization logic:
Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> {
if(exception instanceof SystemExitEvent){
System.exit(((SystemExitEvent)exception).exitCode);
}
})
// in "main flow" or "close button" or whatever
public void mainFlow(){
try {
businessLogic();
Utilities.exit(0);
}
finally {
cleanUpFileSystemOrDatabaseConnectionOrWhatever();
}
}
//...
class Utilities {
// I'm not a fan of documentaiton,
// but this method could use it.
public void exit(int exitCode){
throw new SystemExitEvent(exitCode);
}
}
class SystemExitEvent extends Throwable {
private final int exitCode;
public SystemExitEvent(int exitCode){
super("system is shutting down")
this.exitCode = exitCode;
}
}
_
この戦略には、このロジックをテスト可能にする「利点」が追加されています。「メインフロー」を含むメソッドが実際にシステムの終了を要求することをテストするには、スロータイプをキャッチして、書き込みタイプをアサートするだけです。たとえば、ビジネスロジックラッパーのテストは次のようになります。
_//kotlin, a really Nice language particularly for testing on the JVM!
@Test fun `when calling business logic should business the business`(){
//setup
val underTest = makeComponentUnderTest(configureToReturnExitCode = 42);
//act
val thrown: SystemExitEvent = try {
underTest.mainFlow();
fail("System Exit event not thrown!")
}
catch(event: SystemExitEvent){
event;
}
//assert
assertThat(thrown.exitCode).isEqualTo(42)
_
この戦略の主な欠点は、例外フローから機能を取得する方法であり、意図しない結果をもたらすことが多いことです。この場合、最も明らかなことは、try { ... } catch(Throwable ex){ /*doesnt rethrow*/ }
を記述したすべての場所を更新する必要があることです。カスタム実行コンテキストを備えたライブラリの場合、この例外を理解するために改造する必要があります。
バランス上、これは私にとって良い戦略のようです。ここの他の誰かがそう思いますか?
以下の例では、System.exit(0)
が例外行の前にある場合、プログラムは正常に終了するため、FINALLYは実行されません。
System.exix(0)
がtryブロックの最後の行である場合、ここには2つのシナリオがあります
。
package com.exception;
public class UserDefind extends Exception {
private static int accno[] = {1001,1002,1003,1004,1005};
private static String name[] = {"raju","ramu","gopi","baby","bunny"};
private static double bal[] = {9000.00,5675.27,3000.00,1999.00,1600.00};
UserDefind(){}
UserDefind(String str){
super(str);
}
public static void main(String[] args) {
try {
//System.exit(0); -------------LINE 1---------------------------------
System.out.println("accno"+"\t"+"name"+"\t"+"balance");
for (int i = 0; i < 5; i++) {
System.out.println(accno[i]+"\t"+name[i]+"\t"+bal[i]);
//rise exception if balance < 2000
if (bal[i] < 200) {
UserDefind ue = new UserDefind("Balance amount Less");
throw ue;
}//end if
}//end for
//System.exit(0);-------------LINE 2---------------------------------
}//end try
catch (UserDefind ue)
{
System.out.println(ue);
}
finally{
System.out.println("Finnaly");
System.out.println("Finnaly");
System.out.println("Finnaly");
}
}//end of main
}//end of class