1. 序章

このチュートリアルでは、Javaのthrowthrowsを見ていきます。 それぞれをいつ使うべきかを説明します。

次に、それらの基本的な使用法の例をいくつか示します。

2. スローおよびスロー

簡単な紹介から始めましょう。 これらのキーワードは、例外処理に関連しています。 アプリケーションの通常のフローが中断されると、例外が発生します。

多くの理由があるかもしれません。 ユーザーが間違った入力データを送信する可能性があります。 接続が失われたり、その他の予期しない状況が発生したりする可能性があります。 優れた例外処理は、これらの不快な瞬間が発生した後もアプリケーションを機能させ続けるための鍵です。

throwキーワードを使用して、コードから例外を明示的にスローします。 これは、任意のメソッドまたは静的ブロックの場合があります。 この例外は、次のサブクラスである必要があります投げることができます。 また、それはすることができますスロー可能自体。 1つのthrowで複数の例外をスローすることはできません。

Throwsキーワードをメソッド宣言に配置できます。 これは、このメソッドからスローできる例外を示します。 これらの例外はtry-catchで処理する必要があります。

これらの2つのキーワードは互換性がありません!

3. Javaにをスローします

メソッドから例外をスローする基本的な例を見てみましょう。

まず、簡単な電卓を書いていると想像してみてください。 基本的な算術演算の1つは除算です。 そのため、この機能を実装するように依頼されました。

public double divide(double a, double b) {
    return a / b;
}

ゼロで除算できないため、既存のコードにいくつかの変更を加える必要があります。 例外を発生させるのに良い瞬間のようです。

これをやろう:

public double divide(double a, double b) {
    if (b == 0) {
        throw new ArithmeticException("Divider cannot be equal to zero!");
    }
    return a / b;
}

ご覧のとおり、私たちはArithmeticExceptionをニーズに完全に適合させて使用しました。 例外メッセージである単一のStringコンストラクターパラメーターを渡すことができます。

3.1. 良い習慣

常に最も具体的な例外を優先する必要があります。例外的なイベントに最適なクラスを見つける必要があります。 たとえば、投げる NumberFormatException それ以外の IllegalArgumentException。 不特定のものを投げることは避けなければなりません例外

たとえば、java.langパッケージにはIntegerクラスがあります。 ファクトリメソッド宣言の1つを見てみましょう。

public static Integer valueOf(String s) throws NumberFormatException

これは静的ファクトリメソッドであり、 整数からのインスタンス弦。 間違った入力の場合 、メソッドはスローします NumberFormatException。

独自の、より説明的な例外を定義することをお勧めします。 私たちの中で電卓たとえば、クラス DivideByZeroException。 

サンプルの実装を見てみましょう。

public class DivideByZeroException extends RuntimeException {

    public DivideByZeroException(String message) {
        super(message);
    }
}

3.2. 既存の例外のラッピング

既存の例外を、自分で定義した例外にラップしたい場合があります。

独自の例外を定義することから始めましょう:

public class DataAcessException extends RuntimeException {
    
    public DataAcessException(String message, Throwable cause) {
        super(message, cause);
    }
}

コンストラクターは、例外メッセージと原因の2つのパラメーターを取ります。これは、次の任意のサブクラスである可能性があります。 投げることができます。 

findAll()関数の偽の実装を書いてみましょう。

public List<String> findAll() throws SQLException {
    throw new SQLException();
}

ここで、 SimpleService でリポジトリ関数を呼び出します。これにより、 SQLException:が発生する可能性があります。

public void wrappingException() {
    try {
        personRepository.findAll();
    } catch (SQLException e) {
        throw new DataAccessException("SQL Exception", e);
    }
}

再投げています SQLException と呼ばれる私たち自身の例外に包まれました DataAccessException。 すべてが次のテストによって検証されます。

@Test
void whenSQLExceptionIsThrown_thenShouldBeRethrownWithWrappedException() {
    assertThrows(DataAccessException.class,
      () -> simpleService.wrappingException());
}

これを行う理由は2つあります。 まず、例外ラッピングを使用します。これは、コードの残りの部分が、システムで発生する可能性のあるすべての例外について知る必要がないためです。

また、上位レベルのコンポーネントは、下位レベルのコンポーネントや、それらがスローする例外について知る必要はありません。

3.3. Javaとのマルチキャッチ

時々、私たちが使用するメソッドは、多くの異なる例外をスローする可能性があります。

より広範なtry-catchブロックを見てみましょう。

try {
    tryCatch.execute();
} catch (ConnectionException | SocketException ex) {
    System.out.println("IOException");
} catch (Exception ex) {
    System.out.println("General exception");
}

execute メソッドは、 SocketException、ConnectionException、Exceptionの3つの例外をスローできます。最初のcatchブロックは、ConnectionExceptionまたはSocketExceptionをキャッチします。 2番目のキャッチブロックはキャッチします例外または他のサブクラス例外。 覚えておいてください常に最初に、より詳細な例外をキャッチする必要があります。

キャッチブロックの順序を入れ替えることができます。 次に、すべてが Exception でキャッチされるため、SocketExceptionおよびConnectionExceptionをキャッチすることはありません。

4. はJavaにをスローします

メソッド宣言にthrowsを追加します。

以前のメソッド宣言の1つを見てみましょう。

public static void execute() throws SocketException, ConnectionException, Exception

メソッドは複数の例外をスローする場合があります。メソッド宣言の最後でコンマで区切られます。 チェックされた例外とチェックされていない例外の両方をスローします。 それらの違いを以下に説明します。

4.1. チェックされた例外とチェックされていない例外

チェックされた例外は、コンパイル時にチェックされることを意味します。 この例外を処理する必要があることに注意してください。 それ以外の場合、メソッドはthrowsキーワードを使用して例外を指定する必要があります。

最も一般的なチェック済みの例外は次のとおりです。 IOException、FileNotFoundException、ParseException。 FileNotFoundException 私たちが作成するときにスローされる可能性があります FileInputStream からファイル。 

短い例があります:

File file = new File("not_existing_file.txt");
try {
    FileInputStream stream = new FileInputStream(file);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

メソッド宣言にthrowsを追加することで、try-catchブロックの使用を回避できます。

private static void uncheckedException() throws FileNotFoundException {
    File file = new File("not_existing_file.txt");
    FileInputStream stream = new FileInputStream(file);
}

残念ながら、より高いレベルの関数はまだこの例外を処理する必要があります。 それ以外の場合は、この例外をthrowsキーワードを使用したメソッド宣言に含める必要があります。

反対に、 チェックされていない例外は、コンパイル時にチェックされません。 

最も一般的なチェックされていない例外は次のとおりです。 ArrayIndexOutOfBoundsException、IllegalArgumentException、NullPointerException。 

チェックされていない例外は、実行時にスローされます。 次のコードは NullPointerException。 おそらく、Javaで最も一般的な例外の1つです。

null参照でメソッドを呼び出すと、次の例外が発生します。

public void runtimeNullPointerException() {
    String a = null;
    a.length();
}

テストでこの動作を確認しましょう。

@Test
void whenCalled_thenNullPointerExceptionIsThrown() {
    assertThrows(NullPointerException.class,
      () -> simpleService.runtimeNullPointerException());
}

このコードとテストはあまり意味がないことを覚えておいてください。 実行時の例外を説明するのは学習目的のみです。

Javaでは、ErrorおよびRuntimeExceptionのすべてのサブクラスはチェックされていない例外です。 チェックされた例外は、Throwableクラスの他のすべてです。

5. 結論

この記事では、2つのJavaキーワードの違いについて説明しました。 投げるとスローします。 基本的な使用法を確認し、グッドプラクティスについて少し話しました 。 次に、チェックされた例外とチェックされていない例外について説明しました。

いつものように、ソースコードはGitHubにあります。

Javaでの例外処理について詳しく知りたい場合は、Java例外に関する記事を参照してください。