web-dev-qa-db-ja.com

JavaのSystem.exit()は、try / catch / finallyブロックでどのように機能しますか?

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または、実験を実行できない場合。

66
Thomas Owens

いいえ。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);
    }
  }

}
76
erickson

catchを含む簡単なテストでも、system.exit(0)がセキュリティ例外をスローしない場合、最後に実行されたステートメントになることがわかります(catchおよびfinallyはまったく実行されます)。

system.exit(0)がセキュリティ例外をスローする場合、catchおよびfinallyステートメントが実行されます。 catchfinallyの両方にsystem.exit()ステートメントが含まれる場合、これらのsystem.exit()ステートメントの前のステートメントのみが実行されます。

上記の両方のケースで、tryコードが別のメソッドによって呼び出されたメソッドに属している場合、呼び出されたメソッドは戻りません。

詳細 ここ (個人ブログ)。

8

他の回答では、System.exitcatchをスローせずにJVMを終了する場合にfinallyおよびSecurityExceptionブロックが実行されない方法について説明しましたが、リソースの「try-with-resources」ブロック:リソースは閉じられていますか?

JLS、セクション14.20.3.2 によると:

変換の効果は、リソース仕様をtryステートメントの「内側」に置くことです。これにより、拡張されたtry-with-resourcesステートメントのcatch節が、リソースの自動初期化またはクローズによる例外をキャッチできます。

さらに、finallyキーワードの意図に沿って、finallyブロックが実行されるまでにすべてのリソースが閉じられます(または閉じようとします)。

つまり、リソースはcloseまたはcatchブロックが実行される前にfinallydになります。 closecatchが実行されなくても、それらが何らかの形でfinallydである場合はどうなりますか?

「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する機会を得ません。 。

6
rgettman

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()ステートメントの後に終了します。

3
Chinmoy

この動作に問題があり、_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*/ }を記述したすべての場所を更新する必要があることです。カスタム実行コンテキストを備えたライブラリの場合、この例外を理解するために改造する必要があります。

バランス上、これは私にとって良い戦略のようです。ここの他の誰かがそう思いますか?

2
Groostav
  1. 以下の例では、System.exit(0)が例外行の前にある場合、プログラムは正常に終了するため、FINALLYは実行されません。

  2. 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
0
honey