예외 처리

상혁몬·2025년 3월 19일

java

목록 보기
12/12
post-thumbnail

예외를 처리하기 위해 try ~ catch, throws 구문을 이용.
예외 처리 방법을 알게 되면 안전하고 유연한 프로그래밍 구사 가능


예외 처리하기

try~catch 기본 구조

try {
	(수행할 문장 1);
    (수행할 문장 2);
    ...
} catch(예외 1) {
	(수행할 문장 1);
    ...
} catch(예외 2) {
	(수행할 문장 1);
    ...
}

try 문 안의 수행할 문장에서 예외가 발생하지 않는다면 catch 문에 문장들은 수행되지 않는다. 하지만 try 문 안의 문장을 수행하는 도중 예외 발생하면 예외에 해당되는 catch 문이 수행됨
숫자를 0을로 나누었을 때 발생하는 예외를 처리

public class Sample {
	public static void main(String[] args) {
    	int c;
        try {
        	c = 4 / 0;
        } catch(ArithmeticException e) {
        	c = -1; // 예외가 발생해 이 문장이 수행됨
        }
    }
}

ArithmeticException e에서 e는 ArithmeticException 클래스의 객체, 즉 예외 객체에 해당한다.
이 예외 객체를 통해 해당 예외 클래스의 변수나 메서드를 호출할 수도 있다.


finally

어떤 예외가 발생하더라도 반드시 실행되어야 하는 부분이 있다면, finally 문을 사용한다.

public class Sample {
	public void shouldBeRun() {
    	System.out.println("ok thanks");
    }
    
   	public static void main(String[] args) {
    	Sample sample = new Sample();
        int c;
        try {
        	c = 4 / 0;
        } catch (ArtihmeticException e) {
         	c = -1;
        } finally {
			sample.shouldBeRun(); // 예외에 상관없이 무조건적으로 실행
		}
    }
}
ok, thanks

finally문은 try 문장 수행 중 예외 발생 여부에 상관없이 무조건 실행된다.
따라서 코드를 실행하면 sample.shouldBeRun() 메서드가 실행됨


예외 활용하기 - RuntimeException과 Exception

예외는 크게 두 가지로 구분된다
1. RuntimeException : 실행 시 발생하는 예외
2. Exception : 컴파일 시 발생하는 예외

RuntimeException

class FoolException extends RuntimeException{
}

public class Sample {
	public void sayMoon(String moon) {
    	if("바보".equals(moon)) {
        	throw new FoolException();
        }
        System.out.println("당신의 별명은 "+moon+" 입니다.");
    }
    
    public static void main(String[] args) {
    	Sample sample = new Sample();
        sample.sayMoon("바보");
        sample.sayMoon("야호");
    }
}

이 프로그램을 실행하면 '바보'라는 입력 값으로 sayMoon 메서드 실행 시 다음과 같은 예외가 발생한다.

Exception in thread "main" FoolException
...

Exception

RuntimeException을 상속하는 FoolException을 Exception 상속으로 변경하기만 하면 Sample 클래스에서 컴파일 오류가 발생한다.
이유는 예측 가능한 CheckedException으로 변경되어 예외 처리를 컴파일러가 강제하기 때문.
따라서 try~catch문을 통해 컴파일 오류를 막을 수 있다.

class FoolException extends Exception {}

public class Sample {
	public void sayMoon(String moon) {
    	try {
        	if("바보".equals(moon)) {
            	throw new FoolException();
            }
            System.out.println("당신의 별명은 "+moon+" 입니다.");
        } catch (FoolException e) {
			System.out.println("FoolException이 발생했습니다.");        
        }
     }
     
    public static void main(String[] args) {
    	Sample sample = new Sample();
        sample.sayMoon("바보");
        sample.sayMoon("야호");
    }
}

예외 던지기

앞 예제에서 sayMoon() 메서드에서 FoolException을 발생시키고 예외 처리도 sayMoon() 메서드에서 했다.
하지만 이렇게 하지 않고 sayMoon()메서드를 호출한 곳에서 FoolException을 처리하도록 예외를 위로 던질 수 있는 방법이 있다.

예외를 위로 던지는 예시

public class Sample {
	public void sayMoon(String moon) throws FoolException {
    	if("바보".equals(moon)) {
        	throw new FoolException();
        }
        System.out.println("당신의 별명은 "+moon+" 입니다.");
    }
    
    public static void main(String[] args) {
    	Sample sample = new Sample();
        try {
        	sample.sayMoon("바보");
            sample.sayMoon("야호");
        } catch (FoolException e) {
        	System.out.println("FoolException이 발생했습니다.");
        }
    }
}

main 메서드에서 try~catch 문으로 sayMoon 메서드에 대한 FoolException 예외를 처리하였다.

FoolException 처리를 sayMoon() 메서드에서 하는 것이 좋을까? 아니면 throws를 이용하여 예외 처리를 main 메서드에서 하는 것이 좋을까?

예외 처리 방식의 차이

sayMoon() 메서드에서 처리하는 것과 main() 메서드에서 예외 처리 하는 것에는 아주 큰 차이가 존재한다.

  • sayMoon() 메서드에서 예외를 처리하는 경우 바보, 야호 두 문장이 모두 수행된다.
    sample.sayMoon("바보") 문장 수행 시 FoolException이 발생하겠지만 그 다음 문장인 sample.sayMoon("야호") 역시 수행된다.
  • 하지만 main() 메서드에서 예외 처리를 한 경우에는 두 번째 문장인 sample.sayMoon("야호")가 수행되지 않는다.
    왜냐면 이미 첫 번째 문장에서 예외가 발생해 catch 문으로 빠져버리기 때문
try {
	sample.sayMoon("바보");
    sample.sayMoon("야호"); // 이 문장 수행 x
} catch (FoolException e) {
	System.out.println("FoolException이 발생했습니다.");
}

예외 처리를 하는 위치는 매우 중요
프로그램 수행 여부를 결정하고, 트랜잭션 처리와도 매우 밀접한 관계가 있다.


정리

1. 예외의 종류

(1) Checked Exception (컴파일 타임 예외)

  • 컴파일러가 체크하는 예외 (반드시 처리 필요)
  • Exception 클래스의 직계 하위 클래스 (ex: IOException, SQLException)
  • 예시: 파일을 읽을 때 파일이 없을 경우 (FileNotFoundException)

(2) Unchecked Exception (런타임 예외)

  • 컴파일러가 체크하지 않는다 (처리 선택적)
  • RuntimeException의 하위 클래스(ex: NullPointerException, ArrayIndexOutOfBoundsException)
  • 예시: 배열 범위 초과, null 객체 접근

(3) Error

  • 시스템 레벨의 심각한 문제, 개발자가 처리할 수 없고, 애플리케이션 종료 유발

2. 예외 처리 기본 문법

try {
    // 예외가 발생할 수 있는 코드
    FileReader file = new FileReader("test.txt");
} 
catch (FileNotFoundException e) {
    // 특정 예외 처리 (여러 개 사용 가능)
    System.out.println("파일을 찾을 수 없습니다!");
} 
catch (Exception e) {
    // 일반 예외 처리 (가장 마지막에 위치)
    e.printStackTrace();
} 
finally {
    // 예외 발생 여부와 무관하게 실행 (리소스 정리)
    System.out.println("항상 실행됩니다.");
}

3. 주요 키워드

  • throw : 예외를 강제로 발생 시키는 키워드
if (balance < 0) {
	throw new IllegalArgumentException("잔액은 음수일 수 없습니다.");
}
  • throws : 예외를 호출자에게 전파
public void readFile() throws IOException {
	// 파일 읽기 로직
}

0개의 댓글