본문 바로가기

Java의 정석 : 3rd Edition

[Java의 정석 - 연습문제] Chapter08. 예외처리

Java의 정석 : 3rd Edition, 2016을 개인 학습용으로 정리한 내용입니다.
"ppt 파일 자료, 연습문제"를 학습 용도로 배포할 수 있음을 고지하셨습니다.
저자님께 감사드립니다.

 

 

 

 

[8-1] 예외처리의 정의와 목적에 대해서 설명하시오.

 

나의 답 : 

- 정의 : 프로그램을 실행할 때 발생할 수 있는 예외에 대비하는 코드를 작성하는 것
- 목적 : 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하기 위함.

 

예외처리(exception handling)란

- 정의 : 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것

- 목적 : 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 함.

 

 

 


[8-2] 다음은 실행도중 예외가 발생하여 화면에 출력된 내용이다. 이에 대한 설명 중 옳지 않은 것은?

java.lang.ArithmeticException : / by zero
	at ExceptionEx18.method2(ExceptionEx18.java:12)
	at ExceptionEx18.method1(ExceptionEx18.java:8)
	at ExceptionEx18.main(ExceptionEx18.java:4)

a. 위의 내용으로 예외가 발생했을 당시 호출스택에 존재했던 메서드를 알 수 있다.

b. 예외가 발생한 위치는 method2메서드이며, ExceptionEx18.java파일의 12번째 줄이다.

c. 발생한 예외는 ArithmeticException이며, 0으로 나누어서 예외가 발생했다.

d. method2메서드가 method1메서드를 호출하였고 그 위치는 ExceptionEx18.java파일의 8번째 줄이다.

 

[8-2] 호출스택
[8-2] 호출스택

d : method1()가 method2()를 호출함. (main → method1()  method2())

     - method1() : method2()를 호출한 라인에서 예외 발생

     - main()       : method1()을 호출한 라인에서 예외 발생

 

 

 

 

[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 {}

 

조상보다 많은 수의 예외를 선언할 수 없으므로 자손 예외의 수는 조상과 같거나 적어야 한다.

d : Exception은 모든 예외의 조상이므로 자손에서 선언 불가         (조상 = 확장 가능(더 많은 수의 예외))
e : NumberException은 두 예외의 조상이므로 자손에서 선언 불가(조상 = 확장 가능(더 많은 수의 예외))

 

 

 

 

[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) {}

 

c : Exception(모든 예외의 조상)는 모든 예외를 처리할 수 있어 마지막 catch블럭에 선언해야 한다.

     마지막 catch블럭에 선언하지 않으면, 컴파일 에러가 발생한다. (이후의 문장들이 무의미하기 때문)

 

 

 

 

[8-5] 아래의 코드가 수행되었을 때의 실행결과를 적으시오.

class Exercise8_5 {
    static void method(boolean b) {
        try {
            System.out.println(1);
            if(b) throw new ArithmeticException();
            System.out.println(2);
        } catch(RuntimeException r) {
            System.out.println(3);
            return;
        } catch(Exception e) {
            System.out.println(4);
            return;
        } finally {
            System.out.println(5);
        }
        
        System.out.println(6);
    }

    public static void main(String[] args) {
        method(true);
        method(false);
    } // main
}

 

나의 답 : 

1
3
5
1
2
5
6

 

main() method(true) : try{1}, ArithmeticException 발생 → catch(RuntimeException r){3} → finally{5} → 메서드 종료(return문)
main()  method(false) : try{1, 2} → finally{5} → 6

※ try블럭에서 예외가 발생하면 다음 문장은 수행되지 않고 실행 흐름이 catch블럭으로 이동되어 예외를 처리한다.

※ catch블럭은 발생한 예외에 해당하는 블럭 하나만 수행된 후 try-catch문을 벗어나게 된다.

※ finally블럭은 예외 발생 여부에 상관없이 항상 수행된다. (단, System.exit(0)으로 인해 프로그램 종료될 때는 수행X)

※ try/catch블럭에 return문이 있어도 finally블럭이 수행된 후에 메서드가 종료된다.

 

ArithmeticException는 RuntimeException의 자손이므로 RuntimeException가 선언된 catch블럭에서 처리된다.

 

 

 

 

[8-6] 아래의 코드가 수행되었을 때의 실행결과를 적으시오.

class Exercise8_6 {
    public static void main(String[] args) {
        try {
            method1();
        } catch(Exception e) {
            System.out.println(5);
        }
    }

    static void method1() {
        try {
            method2();
            System.out.println(1);
        } catch(ArithmeticException e) {
            System.out.println(2);
        } finally {
            System.out.println(3);
        }
        
        System.out.println(4);
    }

    static void method2() {
        throw new NullPointerException();
    }
}

 

나의 답 :

3
5

main() method1() method2() : NullPointerException 발생  method1() : finally{3}  main() : catch(Exception e) {5}

- method2() : 예외를 처리할 try-catch블럭이 없으므로 method2()는 종료되고 이를 호출한 method1()으로 되돌아간다.

- method1() : NullPointerException을 처리할 catch블럭이 없으므로 method1()도 종료되고, 이를 호출한 main()로 돌아간다. 이 때 finally블럭이 수행되어 '3'이 출력된다.

- main() : 모든 예외를 처리할 수 있는 catch블럭(Exception)이 있으므로 예외가 처리되고 '5'가 출력된다.

 

 

 

 

[8-7] 아래의 코드가 수행되었을 때의 실행결과를 적으시오.

class Exercise8_7 {
    static void method(boolean b) {
        try {
            System.out.println(1);
            if(b) System.exit(0);
            System.out.println(2);
        } catch(RuntimeException r) {
            System.out.println(3);
            return;
        } catch(Exception e) {
            System.out.println(4);
            return;
        } finally {
            System.out.println(5);
        }
        
        System.out.println(6);
    }

    public static void main(String[] args) {
        method(true);
        method(false);
    }
}

 

나의 답 : 

1

main() method(true) : try{1}, System.exit(0)(프로그램 종료 : 정상 종료이므로 예외 발생X)

변수 b의 값이 true이므로 System.exit(0); 수행되어 프로그램이 즉시 종료된다. 이 때는 finally블럭이 수행되지 않는다.

 

 

 

 

[8-8] 다음은 1~100사이의 숫자를 맞추는 게임을 실행하던 도중에 숫자가 아닌 영문자를 넣어서 발생한 예외이다.

예외처리를 해서 숫자가 아닌 값을 입력했을 때는 다시 입력을 받도록 보완하라.

1과 100사이의 값을 입력하세요 :50
더 작은 수를 입력하세요.
1과 100사이의 값을 입력하세요 :asdf
Exception in thread "main" java.util.InputMismatchException
       at java.util.Scanner.throwFor(Scanner.java:819)
       at java.util.Scanner.next(Scanner.java:1431)
       at java.util.Scanner.nextInt(Scanner.java:2040)
       at java.util.Scanner.nextInt(Scanner.java:2000)
       at Exercise8_8.main(Exercise8_8.java:16)
import java.util.*;

class Exercise8_8 {
    public static void main(String[] args) {
        // 1~100사이의 임의의 값을 얻어서 answer에 저장한다.
        int answer = (int)(Math.random() * 100) + 1;
        int input = 0;  // 사용자 입력을 저장할 공간
        int count = 0;  // 시도횟수를 세기 위한 변수

        do {
            count++;
            System.out.print("1과 100사이의 값을 입력하세요 :");

            input = new Scanner(System.in).nextInt();

            if(answer > input) {
                System.out.println("더 큰 수를 입력하세요.");
            } else if(answer < input) {
                System.out.println("더 작은 수를 입력하세요.");
            } else {
                System.out.println("맞췄습니다.");
                System.out.println("시도횟수는 " + count + "번입니다.");
                break;  // do-while문을 벗어난다
            }
        } while(true);  // 무한반복문
    }
}
// 1과 100사이의 값을 입력하세요 :50
// 더 작은 수를 입력하세요.
// 1과 100사이의 값을 입력하세요 :asdf
// 유효하지 않은 값입니다. 다시 값을 입력해주세요.
// 1과 100사이의 값을 입력하세요 :25
// 더 큰 수를 입력하세요.
// 1과 100사이의 값을 입력하세요 :38
// 더 큰 수를 입력하세요.
// 1과 100사이의 값을 입력하세요 :44
// 맞췄습니다.
// 시도횟수는 5번입니다.

 

나의 답 :

사용자 입력을 받는 부분을 try-catch블럭안에 넣어 예외 처리한다.

에러 메시지를 보면 InputMismatchException예외가 발생했으므로 이를 선언한 catch블럭을 작성한 후

다른 예외도 발생할 수 있으므로 Exception을 선언한 catch블럭을 마지막에 넣었다.

do {
    count++;
    System.out.print("1과 100사이의 값을 입력하세요 : ");

    try {
        input = new Scanner(System.in).nextInt();
    } catch (InputMismatchException e) {
        System.out.println("유효하지 않은 값입니다. 다시 값을 입력해주세요.");
    } catch (Exception e) {
        System.out.println("유효하지 않은 값입니다. 다시 값을 입력해주세요.");
    }
    
    if (answer > input) {
        System.out.println("더 큰 수를 입력하세요.");
    } else if (answer < input) {
        System.out.println("더 작은 수를 입력하세요.");
    } else {
        System.out.println("맞췄습니다.");
        System.out.println("시도횟수는 " + count + "번입니다.");
        break; // do-while문을 벗어난다
    }
} while (true); // 무한반복문

 

해설 :

화면으로부터 값을 입력받는 부분을 try-catch구문으로 예외처리 하면 된다.

※ 사용자로부터 값을 입력받는 경우에는 유효성검사를 철저하게 해야 한다. (사용자가 어떤 값을 입력할지 모르기 때문)

do {
    count++;
    System.out.print("1과 100사이의 값을 입력하세요 : ");
    
    input = new Scanner(System.in).nextInt();

    try {
        input = new Scanner(System.in).nextInt();
    } catch (Exception e) {
        System.out.println("유효하지 않은 값입니다. 다시 값을 입력해주세요.");
        continue;   
    }
    
    if (answer > input) {
        System.out.println("더 큰 수를 입력하세요.");
    } else if (answer < input) {
        System.out.println("더 작은 수를 입력하세요.");
    } else {
        System.out.println("맞췄습니다.");
        System.out.println("시도횟수는 " + count + "번입니다.");
        break; // do-while문을 벗어난다
    }
} while (true); // 무한반복문

 

 

 

 

[8-9] 다음과 같은 조건의 예외클래스를 작성하고 테스트하시오.

[참고] 생성자는 실행결과를 보고 알맞게 작성해야한다.

* 클래스명        : UnsupportedFuctionException

* 조상클래스명 : RuntimeException

* 멤버변수        :

      이   름 : ERR_CODE

      저장값 : 에러코드

      타   입 : int

      기본값 : 100

      제어자 : final private

* 메서드          :

      1. 메서드명 : getErrorCode

          기       능 : 에러코드(ERR_CODE)를 반환한다.

          반환타입 : int

          매개변수 : 없음

          제어자    : public

 

      2. 메서드명 : getMessage

          기       능 : 메세지의 내용을 반환한다.(Exception클래스의 getMessage()를 오버라이딩)

          반환타입 : String

          매개변수 : 없음

          제어자    : public

class Exercise8_9 {
    public static void main(String[] args) throws Exception {
        throw new UnsupportedFuctionException("지원하지 않는 기능입니다.", 100);
    }
}
Exception in thread "main" UnsupportedFuctionException: [100]지원하지 않는 기능입니다.
      at Exercise8_9.main(Exercise8_9.java:5)

 

나의 답 :

- msg : 에러메시지를 저장하는 인스턴스 변수. 상속받은 것이므로 조상 생성자에서 초기화되도록 해야 한다.

- getMessage() : 조상으로부터 상속받은 메서드. ERR_CODE도 같이 출력되도록 하기 위해 오버라이딩했다.

msg가 조상 생성자에서 초기화되었으므로 super.getMessage()를 호출하여 예외 메시지를 얻어야 함.

※ main()에서 printStackTrace()를 호출하지 않았는데 에러 메시지 출력되는 이유

    : JVM의 예외처리기가 getMessage()를 호출했기 때문, main()에서 처리되지 않은 예외는 JVM의 예외처리기가 처리함.

class UnsupportedFuctionException extends RuntimeException {
    private final int ERR_CODE;

    UnsupportedFuctionException(String msg, int errCode) {
        super(msg);                // RuntimeException(String msg) 호출
        ERR_CODE = errCode;
    }

    UnsupportedFuctionException(String msg) {
        this(msg, 100);
    }

    public int getErrCode() {
        return ERR_CODE;
    }

    public String getMessage() {  // Exception.getMessage() 오버라이딩
        return "[" + getErrCode() + "]" + super.getMessage();
    }
}

 

 

 

 

[8-10] 아래의 코드가 수행되었을 때의 실행결과를 적으시오.

class Exercise8_10 {
    public static void main(String[] args) {
        try {
            method1();
            System.out.println(6);
        } catch(Exception e) {
            System.out.println(7);
        }
    }
    
    static void method1() throws Exception {
        try {
            method2();
            System.out.println(1);
        } catch(NullPointerException e) {
            System.out.println(2);
            throw e;
        } catch(Exception e) {
            System.out.println(3);
        } finally {
            System.out.println(4);
        }

        System.out.println(5);
    }

    static void method2() {
        throw new NullPointerException();
    }
}

 

나의 답 :

2
4
7

main() → method1() → method2() : NullPointerException 발생 → method1() : catch(NullPointerException e) {2} →  throw e → finally{4} → main() : catch(Exception e) {7}

- method2() : 예외를 처리할 try-catch블럭이 없으므로 method2()는 종료되고 이를 호출한 method1()으로 되돌아간다.

- method1() : catch블럭(NullPointerException)이 있으므로 예외 처리 되었으나, throw로 다시 발생시켰다.

  예외가 발생한 catch블럭 내에 예외를 처리할 try-catch블럭이 없으므로 method1()가 종료되고, 이를 호출한 main()에 예외가 전달된다. finally블럭이 수행되어 '4'가 출력된다.

- main() : 모든 예외를 처리할 수 있는 catch블럭(Exception)이 있으므로 예외가 처리되고 '7'가 출력된다.