본문으로 바로가기

예외처리(Exception Handling)란?

category 프로그래밍/Java 2021. 11. 14. 20:01

 

예외처리(exception handling)

 예외처리란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리를 하는 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스러운 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것을 의미한다.

 

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

 목적   프로그램의 비정상 종료를 막고, 정상적인 실행상태를 유지하는 것

 

 

프로그램 에러

 프로그램이 실행 중 어떤 원인에 의해서 오작동을 하거나 비정상적으로 종료되는 경우가 있는데, 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류라고 한다.

 에러는 발생시점에 따라 '컴파일 에러(compile-time error)'와 '런타임 에러(runtime error)'로 나눌 수 있는데, 컴파일 에러는 컴파일을 할 때 발생하는 에러를 말하고, 런타임 에러는 프로그램 실행 도중 발생하는 에러를 의미한다. 이 외에도 '논리적 에러(logical error)'가 있는데, 컴파일과 실행 모두 동작하지만 의도한 것과 다르게 동작하는 것을 말한다.

 

 컴파일 에러   컴파일 시에 발생하는 에러

 런타임 에러   프로그램 실행 시에 발생하는 에러

 논리적 에러   실행은 되지만, 의도와 다르게 동작하는 것

 

자바에서는 실행 시(runtime) 발생할 수 있는 프로그램 오류를 '에러(error)'와 '예외(exception)', 두 가지로 구분한다.

 

 에러(error)         프로그램 코드에 의해서 수습될 수 없는 심각한 오류

 예외(exception)   프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류

 

에러는 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류를 의미하고, 예외는 발생하더라도 수습될 수 있는 비교적 덜 심각한 오류를 의미한다.

 

 

예외 클래스의 계층구조

자바에서는 실행 시 발생할 수 있는 오류(Exception과 Error)를 클래스로 정의하였다. 모든 클래스의 조상은 Object 클래스이므로 Exception과 Error 클래스 역시 Object 클래스의 자손들이다.

 

Exception과 Error 클래스의 구조

모든 예외의 최고 조상은 Exception 클래스이며, 상속 계층도를 Exception 클래스부터 도식화하면 다음과 같다.

 

Exception 클래스와 RuntimeException 클래스 중심의 상속 계층도

위 그림에서 볼 수 있듯이 예외 클래스들은 다음과 같이 두 그룹으로 나누어 질 수 있다.

 

Exception 클래스와 그 자손들

RuntimeException 클래스와 그 자손들

 

 

예외 처리하기(try-catch문)

try {
	// 예외가 발생할 수 있는 코드
    } catch (처리할 예외 타입 e) {
	// try 블록 안에서 예외가 발생했을 때 예외를 처리하는 코드
    }

try 블록에는 예외가 발생할 가능성이 있는 코드를 작성한다. 만약 try 블록 안에서 예외가 발생하면 바로 catch 블록이 수행된다. catch문의 괄호( ) 안에 쓰는 예외 타입은 예외 상황에 따라 달라진다.

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

public class ExceptionHandling {
	public static void main(String[] args) {
    	try {
        	FileInputStream fis = new FileInputStream("a.txt");
        } catch (FileNotFoundException e) {
        	System.out.println(e); // 예외 클래스의 toString() 메서드 호출
        }
        System.out.println("여기는 수행됩니다."); // 정상 출력
    }
 }

출력화면

 예외가 발생했을 때 FileNotFoundException e의 toString() 메서드가 호출하는 코드이다. 출력 결과를 살펴보면 첫 번째 줄은 e의 출력 내용이다. 만약 여기서 바로 비정상 종료되었다면 다른 수행이 일어나지 않았을 것이다. 하지만 두 번째 줄 '여기는 수행됩니다.'가 출력되었으므로 예외처리 후에도 프로그램이 계속 수행되었음을 알 수 있다.

 

 

예외 처리하기(try-catch-finally문)

try {
	// 예외가 발생할 수 있는 코드
    } catch (처리할 예외 타입 e) {
	// try 블록 안에서 예외가 발생했을 때 예외를 처리하는 코드
    } finally {
	// 예외처리와 상관 없이 항상 수행되는 코드
    }

finally 블록은 try 블록이 수행되고 어떤 경우에도 반드시 수행된다.

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

public class ExceptionHandling2 {
	public static void main(String[] args) {
		FileInputStream fis = null;
	
	try {
		fis = new FileInputStream("a.txt");
	} catch (FileNotFoundException e) {
		System.out.println(e);
		return;
	} finally {
		if(fis != null) {
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		System.out.println("항상 수행됩니다.");
	}
	System.out.println("여기도 수행됩니다.");
  }
}

출력화면

 위 코드를 보면 우선 입력받은 파일이 없는 경우에 대해 try-catch문을 사용하여 FileNotFoundException 예외 처리를 수행하였다. 프로그램을 실행하면 a.txt 파일이 없으므로 예외가 발생하여 catch 블록이 수행된다. 예외를 출력하고 강제로 return을 수행했지만 출력 결과를 보면 return문과 상관없이 finally 블록이 수행되어 '항상 수행됩니다.' 라는 문장이 출력된 것을 알 수 있다. finally 블록에서도 파일 리소스를 닫는 코드를 구현했는데, 파일 입력 스트림을 닫는 fis.close(); 문장에서도 예외가 발생할 수 있으므로 예외 처리를 하기 위함이다.

 

 

예외 되던지기(exception re-throwing)

 한 메서드에서 발생할 수 있는 예외가 여러 개인 경우, 일부는 try-catch문을 통해 처리하고, 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써 양쪽에서 예외를 나누어 처리할 수 있다. 또한 단 하나의 예외에 대해서도 예외가 발생한 메서드와 호출한 메서드 양쪽에서 처리하도록 할 수 있다. 이것은 예외를 처리한 후 인위적으로 다시 발생시키는 방법을 통해서 가능한데, 이것을 '예외 되던지기'라고 한다.

 

 먼저 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고 catch문에서 필요한 작업을 행한 후에 throw문을 사용해서 예외를 다시 발생시킨다. 다시 발생한 예외는 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또다시 처리한다.

 

 이 방법은 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해주어야 할 작업이 있을 때 사용된다. 이 때 주의할 점은 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를 throws에 지정해주어야 한다.

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

}

출력화면

결과에서 알 수 있듯이 method1()과 main 메서드 양쪽의 catch 블럭이 모두 수행되었음을 알 수 있다. method1()의 catch 블럭에서 예외를 처리하고도 throw문을 통해 다시 예외를 발생시키고, 이 예외를 main 메서드에서 한번 더 처리하였다.

 

 

정리

1. 예외처리란 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것

2. 에러는 어쩔 수 없지만, 예외(exception)는 처리해야 한다.

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

변수의 유효 범위(scope)  (0) 2021.06.08
다형성(polymorphism)의 개념  (0) 2021.05.22