Javaの例外インタビューの質問(+回答)

Java例外インタビューの質問(+回答)

1. 概要

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

2. 質問

Q1. 例外とは何ですか?

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

Q2. ThrowおよびThrowsキーワードの目的は何ですか?

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番目の方法は、複数のcatchブロックを実装することです。

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

例外に継承関係がある場合、注意してください。子タイプが最初に来て、親タイプが後に来なければなりません。 これに失敗すると、コンパイルエラーが発生します。

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

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

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

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

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

チェック済みおよび未チェックの例外は、それぞれコンパイル時例外およびランタイム例外とも呼ばれます。

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

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

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

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

  • OutOfMemoryError –メモリが不足しているためにJVMがより多くのオブジェクトを割り当てることができず、ガベージコレクターがより多くのオブジェクトを利用可能にできなかった場合にスローされます

  • StackOverflowError –スレッドのスタックスペースが不足した場合に発生します。これは通常、アプリケーションの再帰が深すぎるためです。

  • ExceptionInInitializerError –静的初期化子の評価中に予期しない例外が発生したことを通知します

  • NoClassDefFoundError –クラスローダーがクラスの定義を読み込もうとして、それが見つからなかった場合にスローされます。通常、必要なclassファイルがクラスパスに見つからなかったためです。

  • UnsupportedClassVersionError – JVMがclassファイルを読み取ろうとし、ファイル内のバージョンがサポートされていないと判断した場合に発生します。通常、ファイルは新しいバージョンの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サブクラスから継承する必要があります。 そのようなクラスがない場合は、親としてExceptionを選択します。

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

伝統的なエラー検出と処理技術は、多くの場合、読み維持するのは難しいと難しいスパゲッティコードにつながります。 ただし、例外を使用すると、アプリケーションのコアロジックを、予期しない何かが発生したときの処理の詳細から分離できます。

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

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

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

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

List 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 {
    void apply(T t) throws Exception;
}
public void processTasks(
  List taks, CheckedFunction 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"));
}

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

Q15. Throws句がないメソッドからチェック済み例外をスローする方法はありますか?

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

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

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

3. 結論

この記事では、例外に関して、Java開発者の技術面接で出される可能性が高い質問のいくつかを調査しました。 これは完全なリストではなく、さらなる調査の開始としてのみ扱う必要があります。

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