라봉이의 개발 블로그

java의 정석 8강. 예외처리(하) 본문

Java/Java의 정석 읽고 정리

java의 정석 8강. 예외처리(하)

Labhong 2018. 6. 7. 17:32
반응형

1.7 예외의 발생과 catch 블럭


instanceof 연산자로 catch 블록의 () 안의 클래스와 비교해서 맞는 catch 블록의 {} 코드들을 실행함.

예외가 발생했을 때 생성되는 예외클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨져 있으며 다음 함수로 확인이 가능하다.


printStackTrace() - 예외발생 당시의 호출스택에 있었던 메서드의 정보예외 메세지를 화면에 출력

getMessage() - 발생한 예외클래스의 인스턴스에 저장된 메세지를 읽을 수 있다.


public class Main {
    public static void main(String[] args) {
        PrintStream ps = null;

        try {
            ps = new PrintStream("error.log");

            System.out.println(1);
            System.out.println(2);
            System.out.println(3);
            System.out.println(0/0);        // 예외 발생
            System.out.println(4);
        } catch (Exception ae) {
            ae.printStackTrace(ps);         // 호출 스택의 내용을 화면 대신 error.log 파일에 저장
            ps.println("예외 메세지: " + ae.getMessage());       // 마찬가지로 오류 메세지를 error.log 파일에 저장
        }
        System.out.println(6);
    }
}

출력결과:

1

2

3

6

error.log:

java.lang.ArithmeticException: / by zero

at com.company.Main.main(Main.java:15)

예외 메세지: / by zero


오류 메세지의 결과PrintStream으로 전달하는 예제이다.

하지만 위의 PrintStreammain 메서드 외에 다른 곳은 접근할 수가 없기 때문에 이럴 땐 'System.err'를 이용하면 된다.

public class Main {
    public static void main(String[] args) {
        PrintStream ps = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("error.log", true);  // error.log 파일 append를 true로 설정
            ps = new PrintStream(fos);
            System.setErr(ps);          // err의 출력을 화면이 아닌 error.log로 변경한다.

            System.out.println(1);
            System.out.println(2);
            System.out.println(3);
            System.out.println(0/0);
            System.out.println(4);
        } catch (Exception ae) {
            System.err.println("----------------------------");
            System.err.println("예외발생시간: " + new Date());
            ae.printStackTrace(System.err);
            System.err.println("예외메세지: "+ ae.getMessage());
            System.err.println("----------------------------");
        }
        System.out.println(6);
    }
}

출력 결과:

1

2

3

6


error.log:

----------------------------

예외발생시간: Tue Jun 05 16:47:05 KST 2018

java.lang.ArithmeticException: / by zero

at com.company.Main.main(Main.java:19)

예외메세지: / by zero

----------------------------


System.out이나 System.err는 프로그램 어디서든 접근 가능하다.

둘 다 콘솔창 출력이 디폴트이기 때문에 SetErr 메서드로 error.log 파일로 출력을 옮긴 것이다.



1.8 finally 블럭


finally 블럭은 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 한다.

try-catch-finally 순서로 구성된다.

try{
} catch(Exception e){
} finally {
  // 예외의 발생여부와 관계없이 항상 수행되어야할 문장들을 넣는다.
}


try 문에서 return이 발생하여 메서드가 종료되더라도 finally문장은 실행된다.



1.9 메서드에 예외 선언하기


try-catch문 외에 예외를 메서드에 선언하는 방법이 있다.

메서드에 예외를 선언하려면, 메서드의 선언부에 키워드 throws를 사용해서 메서드 내에서 발생할 수 있는 예외를 적어두기만 하면 된다. 그리고, 예외가 여러 개일 경우에는 쉼표(,)로 구분한다.

void method() throws Exception1, Exception2, ..., ExceptionN {
  // 메서드의 내용
}


선언 부분에 적어두면 다른 사람이 보았을 때 어떤 대처를 해야하는 지 이해하기 쉽기 때문에 튼튼한 코드를 만들 수 있다.


사실 예외를 메서드의 throws에 명시한다고 메서드가 처리하는 것이 아니라 그 메서드를 호출한 메서드에게 예외를 전달하여 예외처리를 떠맡기는 것이다.

public class Main {
    public static void main(String[] args) throws Exception {
        method1();
    }

    static void method1() throws Exception {
        method2();
    }

    static void method2() throws Exception {
        throw new Exception();
    }
}

출력결과:

Exception in thread "main" java.lang.Exception

at com.company.Main.method2(Main.java:14)

at com.company.Main.method1(Main.java:10)

at com.company.Main.main(Main.java:6)


다음 출력 결과로 다음과 같은 사실을 알 수 있다.

  • 예외가 발생했을 때 모두 3개의 메서드가 호출스택에 있었으며,

  • 예외가 발생한 곳은 맨 윗줄 method2라는 것과

  • main 메서드가 method1()을, 그리고 method1()은 method2()를 호출했다는 것을 알 수 있다.


이렇게 예외를 넘겨줄 순 있지만 결국 어느 한 곳에서는 try-catch문으로 예외를 처리해야 한다.



1.10 예외 되던지기


한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 몇 개는 try-catch문을 통해서 메서드 내에서 자체적으로 처리하고, 그 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽으로 나눠서 처리되도록 할 수 있다.

이것은 예외를 처리한 후에 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 '예외 되던지기'라고 한다.

public class Main {
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            System.out.println("main메서드에서 예외가 처리 되었습니다.");
        }
    }
    static void method1() throws Exception {
        try {
            throw new Exception();      // 예외 발생
        } catch (Exception e) {
            System.out.println("method1메서드에서 예외가 처리 되었습니다.");
            throw e;        // 다시 예외를 밖으로 발생시킨다.
        }
    }
}


결과에서 알 수 있듯이 method1과 main 메서드 양쪽의 catch 블럭이 모두 수행되었음을 알 수 있다.



1.11 사용자정의 예외 만들기


사용자가 필요에 따라 프로그래머가 새로운 예외클래스를 정의하여 사용할 수 있다.

필요에 따라 알맞은 예외 클래스를 선택할 수 있다.

class MyException extends Exception {
    private final int ERR_CODE;         // 생성자를 통해 초기화한다.

    MyException(String msg, int errCode) {  // 생성자
        super(msg);         // 조상인 Exception 클래스의 생성자를 호출한다.
        ERR_CODE = errCode;
    }

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

    public int getErrCode() {
        return ERR_CODE;
    }
}


반응형
Comments