슬기로운 개발생활

온라인 자바 스터디 #9 - 예외 처리

by coco3o
반응형

목표

자바의 예외 처리에 대해 학습하세요.

학습할 것 (필수)

  • 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이는?
  • RuntimeException과 RE가 아닌 것의 차이는?
  • 커스텀한 예외 만드는 방법

자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

자바에서 예외 처리란, 프로그램 실행 중 발생할 수 있는 예기치 못한 예외 발생에 대비한 코드를 작성하는 것이며,

예외처리의 목적은 예외의 발생으로 인한 실행중인 프로그램의 갑작스런 비정상 종료를 막고 실행상태를 유지할 수 있도록 함

예외처리(Exception handling)의
정의 - 프로그램 실행 시 발생할 수 있는 예외 발생에 대비한 코드를 작성하는 것
목적 - 프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

자바의 예외 처리는

  • 예외가 발생한 메소드 내에서 직접 처리하는 방법
  • 예외가 발생한 메소드를 호출한 곳으로 예외 객체를 넘겨주는 방법
  • 사용자 정의 예외를 생성하여 처리하는 방법이 있다.

 


1. 예외가 발생한 메소드 내에서 직접 처리 ( try - catch - finally)

 

try {

	예외 발생 가능성이 있는 문장들;

}catch(예외 타입1 매개변수명) {
	
    예외타입1의 예외가 발생할 경우 처리 문장들;

}catch(예외 타입2 매개변수명) {
	
    예외타입2의 예외가 발생할 경우 처리 문장들;

}finally {
	
    항상 수행할 필요가 있는 문장들;
}

try  블록은  예외가  발생할  가능성이  있는  범위를  지정하는  블록이다.  try  블록은  최소한  하나의  catch  블록이  있어야  하며,

catch 블록은  try  블록  다음에  위치한다.

 

catch  블록의  매개변수는  예외  객체가  발생했을  때  참조하는  변수명으로  반드시  java.lang.Throwable  클래스의

하위  클래스  타입으로  선언되어야  한다.

 

지정된  타입의  예외  객체가  발생하면  try  블록의  나머지  문장들은  수행되지  않고,  자바  가상  머신은  발생한  예외  객체를 

발생시키며  발생한  예외  객체  타입이  동일한  catch  블록을  수행한다.

 

finally  블록은  필수  블록은  아니다.

finally  블록이  사용되면  finally  블록의  내용은  예외  발생  유무나  예외  catch  유무와  상관  없이  무조건  수행된다.

따라서,  데이터베이스나  파일을  사용한  후  닫는  기능과  같이  항상  수행해야  할  필요가  있는  경우에  사용한다.

package study;

public class ExceptionTest {

    public static void main(String[] args) {
        
        String[] name = new String[2];
        
        try {
            name[0] = "아이유";
            System.out.println("이름 : " + name[0]);

            name[1] = "트와이스";
            System.out.println("이름 : " + name[1]);

            name[2] = "헤이즈";
            System.out.println("이름 : " + name[2]);
            
        }catch (ArrayIndexOutOfBoundsException e) {
        
            System.out.println("배열 참조 에러 발생");
            
        }catch (Exception e) {
        
            System.out.println(e.getMessage());
            
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
이름 : 아이유
이름 : 트와이스
배열 참조 에러 발생
시스템 종료

Process finished with exit code 0

- String[2]  배열을  선언하고,  3번째  배열에  값을  대입할 때  예외가  발생한다.

- ArrayIndexOutOfBoundsException  이  발생하여  catch에  잡히고  "배열 참조 에러 발생"  이라는  메시지가  출력된다.

- 최종적으로  finally가  실행되며  "시스템 종료"  메시지가 출력된다.


2. 예외가 발생한 메소드를 호출 한 곳으로 예외 객체를 넘기는 방법 (throws)

 

자바의 예외 처리 방법은 예외가 발생한 지점에서 try-catch 또는 try-catch-finally 블록을 이용하여

직접 처리하지 않아도 예외가 발생한 메소드를 호출한 지점으로 예외를 전달하여 처리하는 방법이 있다.

 

이때 사용하는 예약어가 throws 이다.

 

throws는 어떠한 메소드의 내부 소스코드에서 에러가 발생했을시 예외처리를 try-catch 로 하는 것이 아니라,

이 메소드를 사용하는 곳으로 책임을 전가하는 행위이다.

package study;

public class ExceptionTest {

    static void callDriver() throws ClassNotFoundException {
        Class.forName("oracle.jdbc.driver.OracleDriver");
        System.out.println("완료");
    }
    public static void main(String[] args) {

        try {

            callDriver();

        }catch (ClassNotFoundException e) {

            System.out.println("클래스를 찾을 수 없습니다.");

        }catch (Exception e) {

            System.out.println(e.getMessage());
            
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
클래스를 찾을 수 없습니다.
시스템 종료

Process finished with exit code 0

- main에서 callDriver() 메소드를 실행시킨다.

- callDriver() 메소드에서 "oracle.jdbc.driver.OracleDriver" 클래스를 가져온다.

- 해당 클래스를 찾지 못하면 ClassNotFoundException이 발생하는데, callDriver()에서는 throws ClassNotFoundException

처리로 호출한 main에 예외를 넘긴다.

- main에서는 ClassNotFoundException을 받아 catch 문에서 잡아서 "클래스를 찾을 수 없습니다." 메시지를 출력한다.

- 마지막으로 finally가 실행되며 "시스템 종료"를 출력한다.


3. 사용자 정의 예외 생성 (throw)

 

기존의 예외 클래스로 예외를 처리할 수 없다면 사용자가 직접 예외 클래스를 작성해서 발생시킬 수 있다.

사용자가 예외 클래스를 직접 정의하려면 예외 클래스의 최상위 클래스인 java.lang.Exception 클래스를 상속받아 클래스를 정의한다.

class 예외클래스이름 extends Exception {

자바 가상 머신은 프로그램 수행중에 예외가 발생하면 자동으로 해당하는 예외 객체를 발생시킨 다음 catch 블록을 수행한다.

 

하지만 예외는 사용자가 강제적으로 발생시킬 수도 있다. 예외를 발생시키기 위해 throw 예약어를 사용한다.

throw new 예외클래스이름(매개변수);

throw를 이용한 예외 발생시에도 try-catch-finally 구문을 이용한 예외 처리(1번)를 하거나,

package study;

public class ExceptionTest {

    public static void main(String[] args) {

        try {
            throw new Exception();
        }catch (Exception e) {
            System.out.println(e);
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
java.lang.Exception
시스템 종료

Process finished with exit code 0

throws를 이용하여 예외가 발생한 메소드를 호출한 다른 메소드로 넘기는 예외 처리 방법(2번)을 사용해야 한다.

package study;

public class MyException extends Exception {
    
    public MyException() {
        super("내가 만든 예외");
    }
}
package study;

public class ExceptionTest {

    static void callException() throws MyException {

        throw new MyException();
    }

    public static void main(String[] args) {

        try {
            callException();
        }catch (MyException e) {
            System.out.println(e.getMessage());
        }finally {
            System.out.println("시스템 종료");
        }
    }
}
내가 만든 예외
시스템 종료

Process finished with exit code 0

- MyException 이라는 Exception을 상속받는 예외를 만들었다. 그리고 MyException은 "내가 만든 예외"라는 메시지를 갖는다.

- ExceptionTest의 main이 실행되면 callException() 메소드를 호출한다.

- callException() 메소드는 MyException()을 new로 생성한 후 callException()를 호출한 main으로 던진다.

- MyException을 받은 main은 catch에서 해당 예외를 받아서 예외 메시지를 출력한다. "내가 만든 예외"

- 최종적으로 finally가 실행되어 "시스템 종료"가 출력된다. 

 


예외(Exception)계층 구조

Java에서 Exception은 checkedunChecked 두 가지로 나눌 수 있다. 아래 사진은 Java Exception 클래스 계층 구조를 보여준다.

체크 예외(checked Exception)

실행하기 전에 예측 가능한 예외들을 말하는데, 체크 예외가 발생할 수 있는 메소드를

사용할 경우, 복구가 가능한 예외들이기 때문에 반드시 예외를 처리하는 코드를 작성해야 한다. catch문으로 예외를 잡거나,

throws로 예외를 자신을 호출한 클래스로 던지는 방법으로 해결해야 한다.

대표적인 Exception - IOException, SQLException

 

언체크 예외(unchecked Exception)

실행하고 난 후에 알 수 있는 예외들을 말하는데, 언체크 예외라고 불리는 이유는 명시적으로 예외처리를 강제하지 않기 때문이다.

언체크 예외는 따로 catch문으로 예외를 잡거나, throws로 선언하지 않아도 된다.

프로그램에 오류가 있을 때 발생하도록 의도된 것이다.

대표적인 Exception - NullPointerException, ArrayIndexOutOfBoundException

Exception과 Error의 차이?

Error는 실행 중 일어날 수 있는 치명적 오류. 컴파일 시점에 체크할 수 없고 오류가 발생하면 프로그램은 비정상 종료된다.

 

Exception은 Error보다 비교적 경미한 오류 try-catch를 이용하여 프로그램의 비정상적인 종료를 막을 수 있다.

 

Error는 런타임에서 실행 시 발생되며 전부 예측 불가능한 Unchecked Error에 속한다.

Exception과 다르게 에러가 발생할 경우 코드를 고치지 않고서는 해결이 불가능하다.

 


RuntimeException과 RE가 아닌 것의 차이?

위에서 설명했듯 RuntimeException은 unchecked Exception을 상속한 클래스이고,

RuntimeException이 아닌것은 checked Exception을 상속한 클래스이다.

 

RuntimeException을 상속한 예외들은 명시적인 예외처리를 강제하지 않기 때문에 따로 catch문으로 예외를 잡거나, throws로 선언하지 않아도 된다.

 

RuntimeException이 아닌 것은 checkedException이 발생할 수 있기 때문에 반드시 예외를 처리하는 코드를 함께 작성해야 한다


커스텀한 예외 만들기

기존에 정의된 예외 클래스 이외에 필요에 따라 새로운 예외 클래스를 정의하여 사용할 수 있다.

보통 Exception 클래스를 상속받는 클래스를 만들지만, 필요에 따라서 알맞은 예외 클래스를 상속받아서 만든다.

String 타입의 파라미터를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨준다.

 

 

다음은 잔고 부족 예외를 사용자 정의 클래스로 선언한 것이다.

package study;

public class InsufficientBalanceException extends Exception {

    public InsufficientBalanceException(String msg) {
        super(msg);
    }
}

InsufficientBalanceException은 Exception을 상속받기 때문에 컴파일러에 의해 체크되는 예외가 된다.

 

다음은 은행 계좌(account) 클래스를 작성한 것이다. 출금(withdraw)메소드에서 잔고(balance)와 출금액을 비교해서

잔고가 부족하면 InsufficientBalanceException을 발생시키도록 하였다.

package study;

public class Account {
    private long balance;

    public long getBalance() {
        return balance;
    }
    
    public void setBalance(long balance) {
        this.balance = balance;
    }

    public void deposit(int money) {
        balance += money;
    }

    public void withdraw(int money) throws InsufficientBalanceException {
        if(balance < money) {
            throw new InsufficientBalanceException("잔고 부족 : " + (money - balance) + "원이 모자랍니다.");
        }
        balance -= money;
    }
}

 

 

try 블록에서 예외가 발생하면 예외 객체는 catch 블록의 매개변수에서 참조하게 되므로 매개변수를 이용하면 예외객체의 정보를 알 수 있다. 모든 예외 객체는 Exception을 상속받기 때문에 메소드를 예외 객체에서 호출 할 수 있다.

그 중에서도 가장 많이 사용되는 getMessage()printStackTrace()가 있다.

 

다음은 Account 클래스를 이용해서 예금과 출금을 한다. 출금할 때 withdraw() 메소드를 이용하므로 예외 처리를 해줘야한다.

InsufficientBalanceException 객체의 getMessage()와 printStackTrace()로 예외에 대한 정보를 얻어낸다.

package study;

public class AccountEx {

    public static void main(String[] args) {
        Account act = new Account();

        act.deposit(1_000_000);
        System.out.println("예금액 : " + act.getBalance());

        try {
            act.withdraw(1_500_000);
        }catch (InsufficientBalanceException e) {
            System.out.println(e.getMessage());
            System.out.println();
            e.printStackTrace();
        }
    }
}
예금액 : 1000000
잔고 부족 : 500000원이 모자랍니다.

study.InsufficientBalanceException: 잔고 부족 : 500000원이 모자랍니다.
	at study.Account.withdraw(Account.java:16)
	at study.AccountEx.main(AccountEx.java:12)

Process finished with exit code 0

 

 

references : 자바의정석 2판

hyeonstorage.tistory.com/203  reference-m1.tistory.com/246

palpit.tistory.com/entry/Java-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A0%95%EC%9D%98-%EC%98%88%EC%99%B8

 

반응형

블로그의 정보

슬기로운 개발생활

coco3o

활동하기