본문 바로가기

프로그래밍/Java

예외처리

예외처리

프로그램이 실행 중 예상하지 못한 상황이 발생하여 오작동을 하거나 종료되는 경우가 있는데 발생하게 된 원인을 오류나 예외라고 한다. 이 둘을 구분해보자

오류(Error)

시스템 레벨에서 발생하며 프로그램 코드에 의해 수습될수 없는 심각한 오류로 프로세스에 영향을 준다.
발생 시점에 따라 컴파일 에러, 런타임 에러, 논리적 에러로 나눌 수 있다.

  • 컴파일 에러: 컴파일 시점에 발생하는 에러
  • 런타임 에러: 실행 시점에 발생하는 에러
  • 논리적 에러: 실행은 되었지만 의도한 바와 다르게 동작하는 에러

예외(Exception)

사용자의 잘못된 사용이나 개발자가 잘못 구현한 로직에 의해 발생되며 코드에 의해 수습될 수 있는 미약한 오류이다. 에러와 다르게 예외는 예외처리를 통해 프로그램이 종료되지 않고 동작하도록 처리할 수 있다.
자바에서는 실행 시 발생할 수 있는 에러와 예외를 클래스로 정의하였으며 Error와 Exception클래스 역시 Object의 자식클래스이다.

출처: TCPSCHOOL

Exception 클래스는 크게 RuntimeException 클래스와 그 외의 다른 자식클래스들로 구분할 수 있다.

RuntimeException

  • 치명적인 예외를 발생시키지 않는 예외
  • try / catch로 처리하기보다 프로그램을 작성할 때 예외가 발생하지 않도록 주의해야 한다.

그 외의 Exception 클래스의 자식 클래스들

  • 치명적인 상황을 발생시키는 예외
  • 반드시 try / catch로 예외처리를 해야 한다
  • 컴파일러가 예외처리를 강제하기 때문에 컴파일 단계에서 확인 가능하다

자바에서 예외를 처리하는 방법

자바에서는 실행중 발생하는 예외를 처리하기위해 try, catch, finally문을 사용할 수 있다.

try-catch, finally

try{
    //예외처리를 할 코드
}catch(Exception e1){
    //e1 예외발생시 실행될 코드
}catch(Exception e2){
    //e2 예외발생시 실행될 코드
}
... //...
finally{
    //예외발생 여부와 상관없이 항상 실행될 코드
}

하나의 try블럭에 여러개의 catch로 예외를 처리할 수 있으나 예외의 종류와는 관계없이 하나의 cath블럭만 수행하며 try블럭에서 예외 발생여부와 관계없이 finally블럭을 수행하고 try-catch를 벗어납니다.

try{
    System.out.println("try블럭");
}catch (Exception e){
    System.out.println("예외발생");
}finally {
    System.out.println("finally블럭");
}
System.out.println("try-catch이후 코드");

실행 결과

try블럭
finally블럭
try-catch이후 코드

다중 catch

JDK1.7부터 | 를 사용하여 하나의 catch블럭으로 합칠 수 있게 되었다.

try {
    //...
} catch (IOException | RuntimeException exception) {
     exception.printStackTrace();
}

이때 주의해야 할 점은 다중 catch 블럭에서 처리하는 예외 클래스가 부모와 자식관계에 있으면 부모클래스 하나로 처리가 가능하기 때문에 컴파일 시 에러가 발생한다.

 try {
    //...
} catch (IOException | FileNotFoundException exception) {   // 컴파일에러 발생
                                                            //IOException 하나로 처리 가능
    exception.printStackTrace();
}

try - with - resources

JDK1.7부터 추가된 것으로 I/O와 같이 자원을 반환해줘야 하는 경우와 함께 사용하면 close()를 호출하지 않아도 try블럭을 벗어나면 자동으로 close()를 호출해 준다.

try(자원을 할당하는 코드){
}

try-catch-finally

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class TryCatchFinally {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("tmp.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

IOException이 발생해도 FileInputStream을 닫기위해 finally블럭에 close를 추가해줘야 한다.

try - with - resources

import java.io.FileInputStream;
import java.io.IOException;

public class TryCatchResources {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("tmp.txt");) {
            //작업
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

try-with-resources로 자동으로 close()를 호출할 수 있으려면 클래스가 AutoCloseable 인터페이스를 구현한 것이여야 한다.

예외 발생시키기 - throw, throws

자바에서는 throw키워드를 통해 예외를 발생시킬 수 있다.

try {
    throw new IOException("IOException을 고의로 발생");
} catch (IOException ioe) {
    ioe.printStackTrace();
}

실행 결과

java.io.IOException: 고의로 발생
    at math.Test.main(Test.java:10)

메서드에 예외 선언하기

try / catch를 사용하지 않고 throws키워드를 사용하여 예외를 메서드에 선언할 수 있다.

 public class Test {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();   //컴파일러에 의해 예외처리가 강제됨
    }
}

BufferedReader 클래스의 readline()메서드는 IOException을 발생시킬 수 있고 IOException은 강제로 예외처리를 해줘야 한다.
예외가 발생할 수 있는 readLine()메서드를 try/catch를 사용하여 처리할 수도 있지만 throws 키워드로 다음 사용자에게 넘길 수 있다. 예외를 전달받은 메서드가 또다시 자신을 호출한 메서드에게 전달할 수 있으며 제일 마지막에는 main메서드에서도 처리되지 않으면 프로그램이 종료된다.

  • try / catch로 처리
 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
    String s = br.readLine();
} catch (IOException e) {
    e.printStackTrace();
}
  • throws로 처리
public class Test {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();

    }
}

커스텀한 예외 만드는 방법

기존에 정의된 예외 클래스 외에 새로운 예외 클래스가 필요하다면 Exception이나 RuntimeException을 상속받아 새로운 예외클래스를 정의할 수 있다.
Exception클래스를 상속받는 경우 예외 처리가 필요하지 않은 경우라도 예외처리를 해야 한다.

public class ChikenException extends Exception{
    public ChikenException(String message) {    //문자열을 매개변수로 받는 생성자
        super(message); //조상의 생성자 호출
    }
}
public class Test {
    public static void main(String[] args) throws ChikenException {
        String[] foods = {"pasta", "cake", "duck", "chiken", "pizza"};
        for (String food : foods) {
            if (food.equals("chiken"))
                throw new ChikenException("치킨을 먹으면 안되");
            System.out.println(food);
        }
    }
}

실행 결과

pasta
cake
duck
Exception in thread "main" math.ChikenException: 치킨을 먹으면 안되
    at math.Test.main(Test.java:12)

참고 문서

Java의 정석 - 남궁 성
http://www.tcpschool.com/java/intro

'프로그래밍 > Java' 카테고리의 다른 글

Enum 클래스  (0) 2021.01.26
멀티쓰레드 프로그래밍  (0) 2021.01.25
조건문과 반복문  (0) 2021.01.11
인터페이스  (0) 2021.01.09
연산자  (0) 2021.01.06