예외를 처리하기 위해 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 문을 사용한다.
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() 메서드가 실행됨
예외는 크게 두 가지로 구분된다
1. RuntimeException : 실행 시 발생하는 예외
2. Exception : 컴파일 시 발생하는 예외
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
...
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() 메서드에서 예외 처리 하는 것에는 아주 큰 차이가 존재한다.
sample.sayMoon("바보") 문장 수행 시 FoolException이 발생하겠지만 그 다음 문장인 sample.sayMoon("야호") 역시 수행된다.sample.sayMoon("야호")가 수행되지 않는다.try {
sample.sayMoon("바보");
sample.sayMoon("야호"); // 이 문장 수행 x
} catch (FoolException e) {
System.out.println("FoolException이 발생했습니다.");
}
예외 처리를 하는 위치는 매우 중요
프로그램 수행 여부를 결정하고, 트랜잭션 처리와도 매우 밀접한 관계가 있다.
(1) Checked Exception (컴파일 타임 예외)
(2) Unchecked Exception (런타임 예외)
RuntimeException의 하위 클래스(ex: NullPointerException, ArrayIndexOutOfBoundsException)null 객체 접근(3) Error
try {
// 예외가 발생할 수 있는 코드
FileReader file = new FileReader("test.txt");
}
catch (FileNotFoundException e) {
// 특정 예외 처리 (여러 개 사용 가능)
System.out.println("파일을 찾을 수 없습니다!");
}
catch (Exception e) {
// 일반 예외 처리 (가장 마지막에 위치)
e.printStackTrace();
}
finally {
// 예외 발생 여부와 무관하게 실행 (리소스 정리)
System.out.println("항상 실행됩니다.");
}
throw : 예외를 강제로 발생 시키는 키워드if (balance < 0) {
throw new IllegalArgumentException("잔액은 음수일 수 없습니다.");
}
throws : 예외를 호출자에게 전파public void readFile() throws IOException {
// 파일 읽기 로직
}