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