55

この質問にはすでに答えがあります。

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の誰かが答えを知っていて、私が実行できない場合に備えてそれを提供できます。実験。


  • これはテストが困難な極端なケースです。 - JRL
  • 私が提供したコードはありません。しかし、テストを書く時間を得ることについてです(エリクソンのおかげで、もうこれを行う必要はありません)。 - Thomas Owens
  • あなたが尋ねる正確な質問に答えるには、テストするために1行の追加コードが必要です。throw new Exception("Error");直前System.exit(0)その後、コンパイルして、実行可能プログラムから返されるステータスコードを確認します。 - Ki Jéy

6 답변


70

いいえSystem.exit(0)戻りません、そして、finallyブロックは実行されません。

System.exit(int)投げることができるSecurityException。それが起こるなら、finallyブロック意志実行されます。同じプリンシパルが同じコードベースから同じメソッドを呼び出しているので、SecurityException2番目の呼び出しからスローされる可能性があります。


これが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);
    }
  }

}


  • それはまた可能ですStackOverflowException防ぐためにSystem.exitスタックフレームの呼び出しでSystem.exitスタック制限の直前です。このような境界条件はハッカーによって設計されることがあるので、これに依存するセキュリティの不変条件がある場合は、Thread.UncaughtExceptionHandlerそれは徹底的な防御として存在します。 - Mike Samuel
  • さらに付け加えると、OPが彼の質問で提供したのと同じコードブロックが、finally実行中のブロック(Antアプリケーション起動スクリプトとしてAntを使用しているjavaタスク)または実行されていない(アプリケーションが直接を使用して呼び出された場合)javaコンソールから、なしでAnt)この回答に基づいて、アプリケーションがAntの内部から呼び出されたときに、このように表示されると思います。javaタスクaSecurityExceptionスローされます。 - Marcus Junius Brutus
  • StackOverflowException存在しない(定義しない限り)。それはStackOverflowError。重要なちょっとした違い。 - Peter Verhas

7

簡単なテストcatchそれも明らかにsystem.exit(0)セキュリティ例外をスローしません、それは最後に実行されたステートメントになります(catchそしてfinallyまったく実行されません)。

もしsystem.exit(0)セキュリティ例外をスローします。catchそしてfinally文が実行されます。両方ともcatchそしてfinally含むsystem.exit()これらのステートメントの前にあるステートメントのみsystem.exit()文が実行されます。

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

もっと詳しくここに(個人ブログ)


  • 帰属をお世話になりありがとうございます。 :) - Andrew Barber

3

他の回答では、catchそしてfinallyブロックは実行されませんSystem.exitをスローせずにJVMを終了します。SecurityExceptionしかし、それらはリソースに対する "try-with-resources"ブロックで何が起こるかを示していません:彼らは閉じられていますか?

によるJLS、セクション14.20.3.2

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

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

つまり、リソースはcloseaの前にdcatchまたはfinallyブロックラン彼らがいる場合はどうなりますcloseどういうわけかdcatchそしてfinally走らない?

これは、 "try-with-resources"ステートメント内のリソースが閉じられていないことを示すためのコードです。

私はの単純なサブクラスを使用しますBufferedReaderそれは呼び出す前にステートメントを出力しますsuper.close

class TestBufferedReader extends BufferedReader {
    public TestBufferedReader(Reader r) {
        super(r);
    }

    @Override
    public void close() throws IOException {
        System.out.println("close!");
        super.close();
    }
}

それから私は電話のテストケースを設定しますSystem.exittry-with-resourcesステートメントで。

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ブロックが実行されない場合、 "try-with-resources"ステートメントは実行されません。closeそのリソースSystem.exit成功しました。


3

tryブロックがthrowableをスローしたとしても、最終的にblockは実行されます....

最後にblockが実行されないのは、System.exit()メソッドを呼び出すときだけです。

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()ステートメントの後。


1

この行動に問題があると考えていて、自分の行動を細かく制御する必要がある場合System.exit呼び出し、そしてあなたができる唯一のことはあなた自身のロジックでSystem.exit機能をラップすることです。そうすれば、最後にブロックを実行し、出口フローの一部としてリソースを閉じることができます。

私がやろうと思っているのはSystem.exit電話& A私自身の静的メソッドの機能私の実装ではexit私はのカスタムサブクラスを投げるだろうThrowableまたはErrorそして、カスタムUncaught例外ハンドラをで実装します。Thread.setDefaultUncaughtExceptionHandlerその例外を処理します。したがって、私のコードは次のようになります。

//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*/ }更新する必要があります。カスタム実行コンテキストを持つライブラリの場合、この例外を理解するためにそれらを改良する必要があります。

結局、これは私にとって良い戦略のように思えます。ここで他の誰かがそう思いますか?


0

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

  2. あればSystem.exix(0)tryブロックの最後の行です。ここでは2つのシナリオがあります。

    • 例外が存在する場合は、最後にブロックが実行されます
    • 例外が存在しない場合、finallyブロックは実行されません

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

リンクされた質問


関連する質問

最近の質問