자바의 예외 처리에 대해 학습하세요.
NullPointerException
, IllegalArgumentException
등StackOverflowError
, OutOfMemoryError
등Throwable
클래스는 자바의 모든 예외와 에러의 부모 클래스RuntimeException
과 RE가 아닌 예외
로 구분RuntimeException
과 RE가 아닌 예외
로 구분Checked Exception과 Unchecked Exception
Checked Exception | Unchecked Exception | |
---|---|---|
계층 | RE가 아닌 예외 | RuntimeException |
처리 여부 | 반드시 예외 처리 해야함(컴파일 단계에서 확인) | 예외 처리 하지 않아도됨(강제 X) |
예시 | IOException, SQLException, ClassNotFoundException ... | NullPointerException, IllegalArgumentException, IndexOutOfBoundException ... |
Checked Exception
RE가 아닌 예외의 경우 클라이언트가 자신이 발생시킨 예외로부터 회복할 수 있도록 예외 처리 강제한다
코드로 부가적인 작업이 가능한 경우
Unchecked Exception
RuntimeException의 경우 프로그래머의 실수로 발생하며 이 모든 것에 대해 예외처리를 강제하는 것은 코드의 가독성과 생산성을 떨어뜨리기 때문에 예외처리를 강제하지 않는다
NullPointerException
이 발생할 수 있는 모든 곳에 예외처리를 한다면 코드 작성은 물론 읽기도 어려울 것이다
Exceoption e = new Exception("message"); // 발생시킬 예외 인스턴스 생성
throw e; //예외 발생시킨다
throw new Exception("message");
void method() throws Exception1, Exception2 {
//구현부
}
throws
를 하든 try-catch
를 하든 예외를 처리해야 한다try{
//예외가 발생할 수 있는 코드
} catch(Exceoption1 e1) {
//Exceoption1 발생시 실행
} catch(Exceoption2 e2) {
//Exceoption2 발생시 실행 //Multiple catch Blocks try-catch
} catch(Exception3a | Exception3b e3) {
//Exception3a or Exception3b 발생시 실행 //Union catch Blocks
}
catch
블럭에 차례로 instanceof
연산자로 검사true
인 catch
블럭 코드 수행 후 전체 try-catch
문을 빠져나간다try-catch
문을 빠져나오기 때문에, 계층구조상 더 좁은 범위의 예외를 먼저 검사해야하며 그렇지 않을 시 컴파일 에러catch
블럭 없다면 예외처리 불가try
블럭의 코드가 둘 이상의 예외를 던질 수 있으며 각 예외에 대해 다른 처리를 하고 싶을 때 사용|
로 구분해 여러 예외 같은 catch
블럭에서 처리 가능try{
//예외가 발생할 수 있는 코드
} catch(Exception e) {
//Exception 발생시 실행
} finally {
//예외 발생 여부 관계없이 실행
}
Anti-Patterns
finally
블럭에서 return
을 쓰지 말자, try
블럭에서 발생한 예외가 드롭된다 ( finally
통해 정상 종료될 수 있다)finally
블럭에서 throw
를 쓰지 말자, finailly
블럭에서 던지는 예외가 catch
블럭에서 던지는 예외를 삼킨다 (스택 추적 내역에서 catch
블럭이 던진 예외에 대한 정보가 사라진다)AutoCloseable
인터페이스를 구현한 리소스에 대해 close()
명시적 호출 없이 간편히 리소스 해제 가능//java 7
try(Resource1 r1 = new Resource1(); Resource2 r2 = new Resource2()) {
//...
} catch(Exceoption e) {
//예외 발생시
}
//java 9
Resource1 r1 = new Resource1()
try(r1) {
//...
} catch(Exceoption e) {
//예외 발생시
}
;
으로 구분백기선님 라이브 중 예시
JAVA PUZZLERS: Traps, Pitfalls, and Corner Cases
public class JavaPuzzler40 {
// 단순히 src 쪽에 있는 파일을 dest 쪽에 복사하는 코드
static void copy(String src, String dest) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
}
/*
정답은 finally { if (in != null) in.close(); if (out != null) out.close(); }
에서 앞에 있는 close()에 문제가 생기면 뒤에 자원이 닫히지 않는 것 그래서 처리하려면 각각을 try catch로
*/
public class JavaPuzzler40 {
static void copy(String src, String dest) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
if (in != null)
try {
in.close();
} catch (IOException e) {
// 딱히 할 게 없고 로깅이나...
}
if (out != null)
try {
out.close();
} catch (IOException e) {
// 딱히 할 게 없고 로깅이나...
}
}
}
}
/*
위의 코드도 문제가 있다
in.close(); 호출할 때 IOException(IOException은 Checked Exception)이 아니라
런타임 예외가 발생한다면
마찬가지로 아래쪽의 if(out != null) 로 가지 않는다
그래서 어떻게 까지 해야 하냐면
*/
public class JavaPuzzler40 {
static void copy(String src, String dest) throws IOException {
InputStream in = null;
OutputStream out = null;
try { // 요부분
out = new FileOutputStream(dest);
try {
in = new FileInputStream(src);
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
if (in != null)
try {
in.close();
} catch (IOException e) {
// 딱히 할 게 없고 로깅이나...
}
}
} finally { //요부분
if (out != null)
try {
out.close();
} catch (IOException e) {
// 딱히 할 게 없고 로깅이나...
}
}
}
}
/*
try-with-resources 사용시
*/
public class JavaPuzzler40 {
static void copy(String src, String dest) throws IOException {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
byte[] buf = new byte[1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
}
catch
문에서 되던지는 방법예외를 메서드 내부와 외부 양쪽에서 처리하는 예시
package week9;
public class Rethrowing {
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
System.out.println("main catch블럭에서 예외 처리");
e.printStackTrace();
}
}
static void method1() throws Exception {
try{
throw new Exception("method1에서 발생");
} catch(Exception e) {
System.out.println("method1 catch블럭에서 예외 처리");
throw e;
}
}
}
/*
실행결과
method1 catch블럭에서 예외 처리
main catch블럭에서 예외 처리
java.lang.Exception: method1에서 발생
at week9.Rethrowing.method1(Rethrowing.java:16)
at week9.Rethrowing.main(Rethrowing.java:7)
*/
예외 번역 시 예외 되던지기 예시
try {
...// 저수준 추상화를 이용한다
} catch(LowerLevelException e) {
//추상화 수준에 맞게 번역
throw new HigherLevelException(...);
}
출저: 이펙티브 자바 3/E: 아이템73 추상화 수준에 맞는 예외를 던지라
예외 번역 시 연쇄 예외 사용 예시
//예외 연쇄
try {
...// 저수준 추상화를 이용한다
} catch(LowerLevelException cause) {
//저수준 예외를 고수준 예외에 실어 보낸다
throw new HigherLevelException(cause);
}
//예외 연쇄용 생성자
class HigherLevelException extends Exception {
HigherLevelException(Throwable cause){
super(cause);
}
}
출저: 이펙티브 자바 3/E: 아이템73 추상화 수준에 맞는 예외를 던지라
initCause(Throwable cause)
메서드를 통해 등록getCause()
메서드를 통해 예외 원인 반환Exception
상속 -> checked 예외RuntimeException
상속 -> unchecked 예외예외 | 사용 상황 |
---|---|
IllegalArgumentException | 허용하지 않는 값이 인수로 건네졌을 때(null은 따로 NullPointerException으로 처리) |
IllegalStateException | 개체가 메서드를 수행하기에 적절하지 않은 상태일 때 |
NullPointerException | null을 허용하지 않는 메서드에 null을 건넸을 때 |
IndexOutOfBoundsException | 인덱스가 범위를 넘어섰을 때 |
ConcurrentModificationException | 허용하지 않는 동시 수정이 발견됐을 때 |
UnsupportedOperationException | 호출한 메서드를 지원하지 않을 때 |
출저: 이펙티브 자바 3/E: 아이템72 표준 예외를 사용하라
관련 읽을 거리
📑📌📜✏️