Java 예외처리란 : "오류가 나기 전에 방어막 치는것"
자바는 오류를 에러와 예외로 구분한다. 에러는 코드로 수습될 수 없는 오류, 예외는 프로그래머가 미리 예측하여 코드(예외처리 코드)로 수습할 수 있는 오류를 말한다.
예외클래스 계층구조: 실행 시 발생할 수 있는 오류를 클래스로 정의한 것이다.
모든 예외의 조상은 Exception 클래스이며, 그 자손들은 크게 두 분류로 나눌 수 있다.
RuntimeException클래스들 : 프로그래머의 실수로 발생한 예외, 컴파일러가 예외처리를 확인하지 않아 unchecked 예외라고도 한다.
Exception클래스들(런타임예외를 제외한 나머지): 사용자의 실수(입력 데이터 형식이 잘못됨) 등의 외적 요인에 의해 발생한 예외. 컴파일러가 예외처리를 확인하므로 checked 예외라고도 한다.
자바의 예외처리 구문에 대해 알아보자..
❗️try-catch 문
try {예외 발생 가능성이 있는 코드} catch (Exception1 e1) {Exception1이 발생한 경우 처리하는 코드}
catch(Exception2 e2) {Exception2가 발생한 경우 처리하는 코드} ...
하나의 try블럭 다음에는 여러 개의 catch블럭이 올 수 있다. 다만 실제로 발생한 예외의 종류와 같은 한 개의 catch블럭만 처리된다.
catch()안에는 처리하고자 하는 예외와 같은 타입(Exception1)의 참조변수(e1)를 선언해야 한다.
catch 블럭이 여러개일 경우에는 첫번째부터 차례로 내려가면서 instanceof 연산자를 이용한 검사가 진행된다. catch()안의 참조변수로 생성된 예외 클래스의 인스턴스를 참조할 수 있는지 검사하는데, 그 결과가 true인 블럭을 만날때까지 계속된다.
-고의적인 예외 발생 : throw 키워드를 이용한다.
public class ExceptionEx9 {
public static void main(String args[]) {
try {
Exception e = new Exception("메롱");
throw e;
} catch (Exception e) {
System.out.println("에러메세지 : " + e.getMessage());
e.printStackTrace();
//발생한 예외에 대한 정보를 불러오는 메소드.
}
System.out.println("정상 종료");
}
}
실행 결과 :
에러메세지 : 메롱
정상 종료
java.lang.Exception: 메롱 at ExceptionEx9.main(ExceptionEx9.java:4)
-메소드에 예외 선언 : throws 키워드를 이용한다. 메소드의 선언부에 “throws Exception”이 있다면, 이 메소드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다.
public class ExceptionEx12 {
public static void main(String[]args) throws Exception {
method1();
}
static void method1() throws Exception {
method2();
}
static void method2() throws Exception {
try{
throw new Exception("ㅋㅋ나잡아봐라 메롱");}
catch (Exception e) {
System.out.println(e.getMessage() + "(이)라는 예외 발생..");
System.out.println("method2: 예외를 처리했습니다.");
}}
출력 결과 :
ㅋㅋ나잡아봐라 메롱(이)라는 예외 발생..
method2: 예외를 처리했습니다.
예외가 발생한 메소드 자체에서 예외를 처리할 수도 있고, 예외가 발생한 메소드를 호출한 메소드에서 처리할 수도 있다. 또는 여러 메소드가 하나의 예외처리를 분담할 수도 있다.
(예외를 발생시키는(던지는)메소드 : throw (예외) 가 포함됨.
예외를 처리하는 메소드 : try-catch문이 포함됨.)
❗️try-catch-finally 문
public class FinallyTest2 {
public static void main(String args[]) {
FinallyTest2.method1();
System.out.println("메인메소드로 돌아왔습니다.");
}
static void method1() {
try {
System.out.println("method1이 호출되었습니다.");
return;
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
System.out.println("method1- finally 블럭이 실행되었습니다.");
}
}
}
출력 결과 :
method1이 호출되었습니다.
method1- finally 블럭이 실행되었습니다.
메인메소드로 돌아왔습니다.
❗️try-with-resources 문
자바에서 입출력에 사용되는 클래스 중에서는 사용 후 꼭 닫아주어야 하는 것들이 있다고 한다. close()를 이용해서 닫아주는 방법이 있는데, close()가 예외를 발생시킬 경우 문제가 된다.
try-with-resources문은 이 점을 개선하기 위해서 생겼다. try블럭 안에 객체를 생성하는 문장을 넣으면 따로 close()를 호출하지 않아도 try블럭을 벗어나는 순간 자동으로 닫힌다. 그 다음 catch와 finally 블럭이 실행된다.
try (FileInputStream fis = new FileInputStream("score.dat");
DataInputStream dis = new DataInputStream(fis)) {
while(true) {
score = dis.readInt();
System.out.println(score);
sum += score;
}
} catch (EOFException e) {
System.out.println("총점은 "+sum+" 입니다.");
} catch(IOException ie) {
ie.printStackTrace();
}}
❗️예외 되던지기(exception re-throwing)
예외를 처리한 후에 인위적으로 다시 발생시키는 방법이다.
왜 그렇게 하냐면, 한 메소드에서 발생가능한 예외가 여러개인 경우에 몇 개는 그 메소드 내에서 자체적으로 처리하고, 선언부에 나머지를 지정하여 해당메소드를 호출한 메소드에서 처리하게 하려고이다.
더 쉽게 말하면 예외처리를 다른 메소드에게 떠넘길 수 있게 하기 위해서이다.
❗️연결된 예외(chained exception)
예외 A가 다른 예외 B를 발생시켰을 때, A를 B의 원인 예외라고 한다.
A를 원인 예외로 하는 예외 B를 발생시키려면 먼저 두 예외를 생성한 후에 B.initCause(A); 라는 문장으로 원인을 등록해준 후, B 예외를 던지면 된다.
마찬가지로 이런게 왜 있냐고 하면.. checked 예외(Exception클래스들)를 unchecked 예외(RuntimeException 클래스들)로 바꾸는 데 유용하다.
자바가 탄생했을 초기(1990년대) 에는 견고한 코드 작성을 위해 예외처리를 강제했는데(checked 예외 사용) 지금은 그 때와 컴퓨터 환경이 많이 달라져 예외처리가 꼭 필수가 아니게 되었다.
uncheck예외로 바꾸면 예외처리가 선택사항이 되므로, 의미없는 try-catch문의 사용을 줄일 수 있다.