자바의 정석 3판 (8) 연습문제 : 예외처리 편

NtoZ·2023년 3월 20일
0

Java

목록 보기
17/23
post-thumbnail

자바의 정석 3판 Chapter8. 예외처리


8-3. 오버라이딩과 상속

  • 문제
[8-3] 다음 중 오버라이딩이 잘못된 것은? (모두 고르시오)
		void add(int a, int b)
				throws InvalidNumberException, NotANumberException {}

		class NumberException extends Exception {}
		class InvalidNumberException extends NumberException {}
		class NotANumberException extends NumberException {}

        a.    void add(int a, int b) throws InvalidNumberException, NotANumberException {}
        b.	void add(int a, int b) throws InvalidNumberException {}
        c.	void add(int a, int b) throws NotANumberException {}
        d.	void add(int a, int b) throws Exception {}
        e.	void add(int a, int b) throws NumberException {}
  • 내 풀이:
    해당 문제는 메서드 오버라이딩의 주의사항 중 예외의 상속에 관한 내용이다.
    메서드 오버라이딩 시에는 부모 메서드의 예외 개수와 범위를 모두 고려해서,
    자손 메서드가 부모 메서드 보다 같거나 적은 범위의 또는 적은 개수의 예외를 선언할 수 있도록 해야 한다.

    본문의 void add(int a, int b)는 InvalidNumberException, NotANumberException 두 개의 예외를 선언하고 있다.

    ❓a는 옳지 않다. 부모와 같은 범위의 예외를 선언할 수 없다. ➡️ ⭐ 부모 메서드와 자손 메서드 모두 같은 범위의 예외를 선언할 수 있다.
    b는 옳다. 부모 메서드 보다 예외의 개수가 적고, 범위도 좁아졌다.
    c는 옳다. InvalidNumberException에 대한 예외 선언이 없어지고 NotANumberException만 남았다.
    💡d는 옳지 않다. 개수는 줄어들었지만 Exception 예외 선언이 되어 있다. Exception은 모든 예외의 조상이므로, 처리해야하는 예외 개수 자체가 더 많다.
    💡e는 옳지 않다. NumberException은 InvalidNumberException의 상위 클래스이므로 더 많은 예외를 처리할 가능성이 있다.

  • 모범 답안 :
    정답 - d, e
    해설 : 오버라이딩 할 때 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.


8-4. 메서드와 예외처리

  • 문제
[8-4] 다음과 같은 메서드가 있을 때, 예외를 잘못 처리한 것은? (모두 고르시오)
  void method() throws InvalidNumberException, NotANumberException {}
  class NumberException extends RuntimeException {}
  class InvalidNumberException extends NumberException {}
  class NotANumberException extends NumberException {}

a.	try {method();} catch(Exception e) {}
b.	try {method();} catch(NumberException e) {} catch(Exception e) {}
c.	try {method();} catch(Exception e) {} catch(NumberException e) {}
d.	try {method();} catch(InvalidNumberException e) {} catch(NotANumberException e) {}
e.	try {method();} catch(NumberException e) {}
f.	try {method();} catch(RuntimeException e) {}
  • 내 풀이 :
    void method()를 호출하는 메서드에서는 InvalidNumberException과 NotANumberExeption을 예외 처리 하거나 떠넘기기 해야한다.

    1. a는 옳다. Exception이 모든 예외의 조상클래스이므로 catch 구문에서 성공적으로 예외를 잡을 수 있다.
    2. ⭐b는 옳다. NumberException에서는 InvalidNumberException을 잡을 수 있고 NotANumberException이나 다른 예외에 대한 처리는 Exception이 한다.❓
    3. ⭐⭐c는 옳지 않다. 부모 예외와 자손 예외 중 부모 예외가 먼저 catch로 잡게된다면 뒤의 구문은 동작하지 않게 되므로 의미 없는 문법에 대해 오류가 발생한다.
    4. d는 옳다. 요청한 두 예외에 대한 예외 처리가 정상적으로 이뤄진다.
    5. e는 옳다. InvalidNumberException이 NumberException를 상속받았으며, InvalidNumberException이 NumberException을 상속받았으므로 공통조상이다.
    6. f는 옳다. 공통조상인 NumberException이 RuntimeException을 상속받았으므로 공통 조상으로 형변환이 가능하기 때문에 예외를 처리할 수 있다.

의문점

  • 궁금한 점:
    한 코드에서 예외가 동시에 2개 이상 발생할 수는 없나?
    그리고 이 경우에도 Exception 하나가 2개의 예외를 catch할 수 있나?
    그 흐름이 어떻게 되는건지 궁금하다.
    해당 catch블럭에서 예외의 경우의 수에 따라 if문에 따른 처리를 해주면 되는건가?

8-6. 예외 처리 관련 코드의 실행결과

  • 문제: 아래 코드의 수행 결과 적기
class Sol_Exercise8_6 {
    public static void main(String[] args) {
        try {
            method1();              //🔥예외가 발생할 가능성이 있는 메소드
        } catch (Exception e) {
            System.out.println(5);  //🔥모든 예외는 Exception에서 무조건 잡힌다.
        }
    }

    static void method1() {
        try {
            method2();          //🔥method2는 NullPointerException 인스턴스 생성하고 발생시킴
            System.out.println(1);  //동작X
        } catch (ArithmeticException e) {   //NullPointerException이 아니기 때문에 타입매치x
            System.out.println(2);
        } finally { //🔥무조건 실행
            System.out.println(3);
        }

        System.out.println(4);  //🔥✔️✔️try-catch-finally 이후이므로 실행❓
    } // method1()

    static void method2() {
        throw new NullPointerException();
    }
}
  • 내 풀이 :
    main 메서드가 method1을 호출하고 method1이 method2를 호출한다.
    동작은 method2 -> method1 -> main 의 세부내용들이 실행될 것이다.
    3, 4, 5가 실행된다.

  • 틀린 풀이 :
    ✔️ 4는 실행되지 않는다.
    ⭐왜냐하면 method2는 throws가 아닌 throw를 사용해서 예외를 발생시켰기 때문에 try-catch도 없어 비정상 종료된다.
    ⭐그런데 method1에서도 NullPointerException에 대한 예외처리가 되어있지 않다. 비정상 종료된다.
    다만, 그 시점이 중요한데, try의 method2에서 인스턴스 발생 후 try{}블럭은 건너뛰어 catch를 점검한다.
    ⭐⭐catch에는 NullPointerException를 잡을 것이 없으므로 finally (println(3))를 실행하고 🔥비정상 종료🔥된다.
    ⭐main에서는 모든 예외를 잡을 수 있는 Exception이 있으므로 5가 실행된다.

  • 틀린 이유 :
    예외 발생(throw)과 예외 처리(throws)에 대한 착오가 있었다.
    또한 예외처리를 하지 못하면 비정상 종료된다는 것과, 그 종료되는 시점에 대해 명확히 인지하지 못했다.

  • 모범 :

<정답>
3
5
<해설>
main메서드가 method1()을 호출하고, method1()method2()를 호출한다.
method2()에서 NullPointerException이 발생했는데, 이 예외를 처리해줄 try-catch블럭이 없으므로
method2()는 종료되고,
이를 호출한 method1()으로 되돌아갔는데 ⭐여기에는 try-catch블럭이 있긴 하지만
NullPointerException을 처리해줄 catch블럭이 없으므로
method1()도 종료⭐되고, 이를 호출한 main메서드로 돌아간다. 이 때 finally블럭이 수행되어 '3'이 출력된다.

💡System.exit(0)과 예외처리

  • 문제: 아래의 코드가 수행되었을 때의 실행결과를 적으시오.
class Sol_Exercise8_7 {
    static void method(boolean b) {
        try {
            System.out.println(1);  //🔥💧 예외 없음. 실행
            if (b)
                System.exit(0); //🔥 true일 경우 System.exit 예외 발생. RuntimeException인지 Exception인지 알 수 없다.
            System.out.println(2);  //💧 false일 때 2가 실행 예외가 발생하지 않으니 catch를 건너뛰고 finally 실행
        } catch (RuntimeException r) {
            System.out.println(3);  //🔥 true
            return;     // ❓return문이 있어도 finally는 동작? 그럴 것이다. 그러나 try-catch-finally 부분을 실행하고 return될 것.
        } catch (Exception e) {
            System.out.println(4);
            return;
        } finally {     //🔥💧true, false이든 무조건 실행
            System.out.println(5);
        }

        System.out.println(6);  //💡try~catch 블럭 안에서 return이 나오면 여기까지는 도달하지 x
                                //💧catch안의 return이 동작하지 않고 정상 순서로 프로그램이 진행되니 자동 실행
    }

    public static void main(String[] args) {
        method(true);
        method(false);
    } // main
}
  • 내 풀이 & 모범 답안 & 틀린 이유 :
<내 풀이>
main 단에서 method(true);method(false);를 각각 실행했을 때의 결과를 묻고 있다.
🔥method(true)일 때는 1, 3, 5가 실행된다.
💧method(false)일 때는 1, 2, 5, 6이 실행된다.
<내 정답>
1
3
5
1
2
5
6

<모범>
<정답> 1
<해설>
변수 b의 값이 true이므로 System.exit(0);이 수행되어 프로그램이 즉시 종료된다.
이럴 때는 finally 블럭이 수행되지 않는다.

<틀린이유>
💡System.exit(0)은 프로그램 자체를 종료하는 메서드이다.
<궁금점>
❓❓그렇다고 하더라도 method(false)는 실행되어야 하는게 아닌가?
💡 System.exit(0)은 JVM을 강제로 종료시키는 역할을 합니다. 0은 정상적인 종료임을 나타내는 종료 코드입니다.
따라서 해당 코드가 실행되면, 현재 실행 중인 Java 프로그램이 즉시 종료됩니다.
method(true)에서 System.exit(0)이 실행되어 JVM이 강제 종료되면서 프로그램이 바로 종료됩니다.
따라서 method(false)는 실행되지 않습니다.
  • 아이디어 :
    💡💡 차이점을 알 수 있다.
    단순 예외가 발생한 것이었다면 프로그램이 비정상 종료되지 않았을 것이다.
    그러나 System.exit(0)은 JVM 자체를 종료시키는 것이기 때문에 프로그램 자체가 그 즉시 종료된다.

  • 💡[Bonus] 8-9. 조상의 메서드를 오버라이딩할 때는 가능하다면 조상읨 ㅔ서드를 제활용하는 것이 좋다.


profile
9에서 0으로, 백엔드 개발블로그

0개의 댓글