1. 概要

例外は、すべてのJava開発者が精通している必要がある重要なトピックです。 この記事では、インタビュー中に出てくる可能性のあるいくつかの質問に対する回答を提供します。

2. 質問

Q1。 例外とは何ですか?

例外は、プログラムの実行中に発生し、プログラムの命令の通常のフローを中断する異常なイベントです。

Q2。 スローとスローキーワードの目的は何ですか?

throws キーワードは、メソッドが実行中に例外を発生させる可能性があることを指定するために使用されます。 メソッドを呼び出すときに、明示的な例外処理を強制します。

public void simpleMethod() throws Exception {
    // ...
}

throw キーワードを使用すると、例外オブジェクトをスローして、プログラムの通常のフローを中断できます。 これは、プログラムが特定の条件を満たさない場合に最も一般的に使用されます。

if (task.isTooComplicated()) {
    throw new TooComplicatedException("The task is too complicated");
}

Q3。 例外をどのように処理できますか?

try-catch-finally ステートメントを使用する:

try {
    // ...
} catch (ExceptionType1 ex) {
    // ...
} catch (ExceptionType2 ex) {
    // ...
} finally {
    // ...
}

例外が発生する可能性のあるコードのブロックは、tryブロックで囲まれています。 このブロックは、「保護された」または「保護された」コードとも呼ばれます。

例外が発生した場合、スローされた例外に一致する catch ブロックが実行され、そうでない場合、すべてのcatchブロックが無視されます。

finally ブロックは、 try ブロックが終了した後、その内部で例外がスローされたかどうかに関係なく、常に実行されます。

Q4。 複数の例外をどのようにキャッチできますか?

コードブロック内の複数の例外を処理する方法は3つあります。

1つ目は、スローされるすべての例外タイプを処理できるcatchブロックを使用することです。

try {
    // ...
} catch (Exception ex) {
    // ...
}

推奨される方法は、可能な限り正確な例外ハンドラーを使用することです。

例外ハンドラーが広すぎると、コードでエラーが発生しやすくなり、予期しない例外がキャッチされ、プログラムで予期しない動作が発生する可能性があります。

2番目の方法は、複数のキャッチブロックを実装することです。

try {
    // ...
} catch (FileNotFoundException ex) {
    // ...
} catch (EOFException ex) {
    // ...
}

例外に継承関係がある場合は注意してください。 子タイプが最初に来て、親タイプが後で来る必要があります。 これを怠ると、コンパイルエラーが発生します。

3つ目は、マルチキャッチブロックを使用することです。

try {
    // ...
} catch (FileNotFoundException | EOFException ex) {
    // ...
}

この機能は、Java7で最初に導入されました。 コードの重複を減らし、保守を容易にします。

Q5。 チェックされた例外とチェックされていない例外の違いは何ですか?

チェックされた例外は、 try-catch ブロック内で処理するか、throws句で宣言する必要があります。 一方、チェックされていない例外は、処理または宣言する必要はありません。

チェックされた例外とチェックされていない例外は、それぞれコンパイル時と実行時の例外とも呼ばれます。

Error RuntimeException 、およびそれらのサブクラスで示されるものを除いて、すべての例外はチェック済みの例外です。

Q6。 例外とエラーの違いは何ですか?

例外は、回復可能な状態を表すイベントですが、エラーは、通常は回復できない外部の状況を表します。

JVMによってスローされるすべてのエラーは、 Error またはそのサブクラスの1つのインスタンスであり、より一般的なものには次のものが含まれますが、これらに限定されません。

  • OutOfMemoryError – JVMがメモリ不足のため、JVMがそれ以上のオブジェクトを割り当てることができず、ガベージコレクターがそれ以上のオブジェクトを利用可能にできなかった場合にスローされます
  • StackOverflowError –スレッドのスタックスペースが不足した場合に発生します。これは通常、アプリケーションの繰り返しが深すぎるためです。
  • ExceptionInInitializerError –静的初期化子の評価中に予期しない例外が発生したことを通知します
  • NoClassDefFoundError –クラスローダーがクラスの定義を読み込もうとして見つからなかった場合にスローされます。通常、必要なclassファイルがクラスパスに見つからなかったためです。
  • UnsupportedClassVersionError – JVMがクラスファイルを読み取ろうとし、ファイル内のバージョンがサポートされていないと判断した場合に発生します。通常、ファイルは新しいバージョンのJavaで生成されたためです。

エラーはtryステートメントで処理できますが、エラーがスローされた後、プログラムが確実に何かを実行できるという保証がないため、これは推奨される方法ではありません。

Q7。 次のコードブロックを実行すると、どのような例外がスローされますか?

Integer[][] ints = { { 1, 2, 3 }, { null }, { 7, 8, 9 } };
System.out.println("value = " + ints[1][1].intValue());

配列の長さより大きい位置にアクセスしようとしているため、ArrayIndexOutOfBoundsExceptionがスローされます。

Q8。 例外連鎖とは何ですか?

別の例外に応答して例外がスローされたときに発生します。 これにより、提起された問題の完全な履歴を見つけることができます。

try {
    task.readConfigFile();
} catch (FileNotFoundException ex) {
    throw new TaskException("Could not perform task", ex);
}

Q9。 スタックトレースとは何ですか?それは例外とどのように関連していますか?

スタックトレースは、アプリケーションの開始から例外が発生した時点までに呼び出されたクラスとメソッドの名前を提供します。

これは、アプリケーションのどこで例外がスローされたか、および例外が発生した元の原因を正確に特定できるため、非常に便利なデバッグツールです。

Q10。 なぜ例外をサブクラス化したいのですか?

例外タイプがJavaプラットフォームにすでに存在するタイプで表されていない場合、またはより正確な方法で処理するためにクライアントコードに詳細情報を提供する必要がある場合は、カスタム例外を作成する必要があります。

カスタム例外をチェックするかオフにするかは、ビジネスケースに完全に依存します。 ただし、経験則として、 例外を使用しているコードがその例外から回復することが期待できる場合は、チェックされた例外を作成します。それ以外の場合は、チェックを外します。

また、スローするサブクラスに密接に関連する最も具体的なExceptionサブクラスから継承する必要があります。 そのようなクラスがない場合は、親として例外を選択します。

Q11。 例外のいくつかの利点は何ですか?

従来のエラー検出および処理技術では、スパゲッティコードの保守が難しく、読みにくいことがよくあります。 ただし、例外を使用すると、アプリケーションのコアロジックを、予期しないことが発生した場合の処理の詳細から分離できます。

また、JVMは呼び出しスタックを逆方向に検索して、特定の例外の処理に関心のあるメソッドを見つけるためです。 追加のコードを記述せずに、コールスタックでエラーを伝播する機能を取得します。

また、プログラムでスローされるすべての例外はオブジェクトであるため、クラス階層に基づいてグループ化または分類できます。 これにより、 catch ブロックで例外のスーパークラスを指定することにより、単一の例外ハンドラーで例外のグループをキャッチできます。

Q12。 ラムダ式のボディ内に例外をスローできますか?

Javaによってすでに提供されている標準の機能インターフェイスを使用する場合、標準の機能インターフェイスにはメソッドシグネチャに「throws」句がないため、チェックされていない例外のみをスローできます。

List<Integer> integers = Arrays.asList(3, 9, 7, 0, 10, 20);
integers.forEach(i -> {
    if (i == 0) {
        throw new IllegalArgumentException("Zero not allowed");
    }
    System.out.println(Math.PI / i);
});

ただし、カスタム機能インターフェイスを使用している場合は、チェックされた例外をスローすることができます。

@FunctionalInterface
public static interface CheckedFunction<T> {
    void apply(T t) throws Exception;
}
public void processTasks(
  List<Task> taks, CheckedFunction<Task> checkedFunction) {
    for (Task task : taks) {
        try {
            checkedFunction.apply(task);
        } catch (Exception e) {
            // ...
        }
    }
}

processTasks(taskList, t -> {
    // ...
    throw new Exception("Something happened");
});

Q13。 例外をスローするメソッドをオーバーライドするときに従う必要のあるルールは何ですか?

いくつかのルールは、継承のコンテキストで例外を宣言する方法を指示します。

親クラスメソッドが例外をスローしない場合、子クラスメソッドはチェックされた例外をスローできませんが、チェックされていない例外をスローする可能性があります。

これを示すサンプルコードは次のとおりです。

class Parent {
    void doSomething() {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IllegalArgumentException {
        // ...
    }
}

次の例では、オーバーライドするメソッドが、オーバーライドされるメソッドで宣言されていないチェック済みの例外をスローするため、コンパイルに失敗します。

class Parent {
    void doSomething() {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IOException {
        // Compilation error
    }
}

親クラスメソッドが1つ以上のチェックされた例外をスローすると、子クラスメソッドはチェックされていない例外をスローできます。 宣言されたチェック済み例外のすべて、なし、またはサブセット、および同じスコープまたはより狭い範囲を持っている限り、これらの例外の数はさらに多くなります。

前のルールに正常に従うサンプルコードを次に示します。

class Parent {
    void doSomething() throws IOException, ParseException {
        // ...
    }

    void doSomethingElse() throws IOException {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IOException {
        // ...
    }

    void doSomethingElse() throws FileNotFoundException, EOFException {
        // ...
    }
}

どちらの方法もルールを尊重することに注意してください。 1つ目は、オーバーライドされたメソッドよりも少ない例外をスローし、2つ目は、より多くの例外をスローします。 範囲が狭くなります。

ただし、親クラスのメソッドが宣言していないチェック済みの例外をスローしようとした場合、またはより広いスコープで例外をスローした場合。 コンパイルエラーが発生します:

class Parent {
    void doSomething() throws FileNotFoundException {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IOException {
        // Compilation error
    }
}

親クラスメソッドにチェックされていない例外を含むthrows句がある場合、子クラスメソッドは、関連していない場合でも、チェックされていない例外をスローしないか、任意の数だけスローできます。

ルールを尊重する例を次に示します。

class Parent {
    void doSomething() throws IllegalArgumentException {
        // ...
    }
}

class Child extends Parent {
    void doSomething()
      throws ArithmeticException, BufferOverflowException {
        // ...
    }
}

Q14。 次のコードはコンパイルされますか?

void doSomething() {
    // ...
    throw new RuntimeException(new Exception("Chained Exception"));
}

はい。 例外をチェーンする場合、コンパイラはチェーンの最初の例外のみを考慮し、チェックされていない例外を検出するため、throws句を追加する必要はありません。

Q15。 スロー句を持たないメソッドからチェックされた例外をスローする方法はありますか?

はい。 コンパイラーによって実行される型消去を利用して、実際にはチェックされていない例外をスローしていると思わせることができます。 チェックされた例外をスローします:

public <T extends Throwable> T sneakyThrow(Throwable ex) throws T {
    throw (T) ex;
}

public void methodWithoutThrows() {
    this.<RuntimeException>sneakyThrow(new Exception("Checked Exception"));
}

3. 結論

この記事では、例外に関して、Java開発者向けの技術インタビューに表示される可能性のあるいくつかの質問について説明しました。 これは完全なリストではなく、さらなる研究の始まりとしてのみ扱われるべきです。

Baeldungでは、今後のインタビューで成功することを願っています。