https://github.com/whiteship/live-study/issues/9
목차
1. 자바에서 예외처리 방법 (try, catch, throw, throws, finally)
2. 자바가 제공하는 예외 계층 구조
3. Exception과 Error의 차이는?
4. RuntimeException과 RE가 아닌 것의 차이는?
5. 커스텀한 예외 만드는 방법
1. 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
호출스택
에러는 프로그램 실행 중에 오작동을 하거나 비정상적으로 종료되는 원인이다. exception은 에러 중에서도 프로그램 코드에 의해 수습될 수 있는 미약한 오류를 말한다. 에러가 발생하면 exception object가 생성되는데, 어떤 error인지 언제 발생했는지에 대한 정보를 가지고 있다. exception object를 만들고 runtime system에 넘겨주는 것을 throwing an exception이라고 한다. exception을 throw하고 나면 아래 그림처럼 호출스택에서 에러가 발생한 method에서 시작해서 자신을 호출한 순서대로 exception을 처리할 수 있는 method를 찾는다. 만약 호출스택을 전부 확인했는데 아래 그림과 다르게 exception을 처리할 수 있는 method가 없다면 비정상적으로 시스템이 종료된다.
The try-catch Block
exception을 처리하는 첫번째 방법은 try block을 사용하는 것이다. try block에서 exception이 발생하면 뒤에 이어지는 catch block에서 exception handler를 찾는다. catch block은 Throwable class의 자손클래스를 인자로 받는다. try block에서 발생한 exception이 catch block에서 처리할 수 있으면 exception을 처리하고 try-catch문을 빠져나온다.
try {
/*내용 생략*/
} catch (IndexOutOfBoundsException e) {
System.err.println("IndnexOutOfBoundsException: " + e.getMessage());
} catch (IOException e) {
System.err.println("Caght IOException: " + e.getMessage());
} catch (Exception e) {
System.err.println("Exception");
}
The finally Block
finally block은 예외의 발생여부에 상관없이 실행할 코드를 포함시키기 위해 사용한다. catch문과 다르게 try-catch문 뒤에 선택적으로 사용할 수도 있고 사용하지 않아도 된다. 예외가 발생하면 try block, catch block, finally block 순으로 실행되고 발생하지 않으면 try block, finally block순으로 실행된다.
throw
throw를 사용하면 고의로 예외를 발생시킬 수 있다. throw 뒤에는 single argument로 throwable object를 넣으면 된다.
public Object pop() {
Object obj;
if (size == 0) {
throw new EmptyStackException();
}
obj = objectAt(size-1);
setObjectAt(size-1, null);
size--;
return obj;
}
pop은 stack의 마지막 요소를 삭제하고 object을 return하는 메서드인데, 빈 stack일 때 pop을 사용할 수 없으므로 고의로 error를 발생시켰다.
throws
메서드에서 발생한 예외를 호출한 메서드 혹은 더 위에서 처리해야할 때 사용한다. throws 뒤에는 메서드에서 발생할 가능성이 있는 exception을 적을 수 있는데, 뒤에서 언급할 것이지만 checked exception을 적어준다. RuntimeException의 자손인 unchecked exception은 적어도 되지만 필수는 아니다.
ex)
public void writeList() throws IOException, IndexOutOfBoundsException {
public void writeList() throws IOException {
multi-catch
다른 예외지만 처리하는 방법이 같아서 중복을 제거하고 싶을 때 사용한다.
public class ExceptionTest {
public static void main(String[ ] args) {
try {
System.out.println(1/0);
} catch (IllegalArgumentException | ArithmeticException e) {
System.out.println(e.getMessage());
}
}
}
catch 문 안에 상속관계를 적어주면 (RuntimeException | IllegalArgumentException e) 와 같이 적어주면 컴파일 에러가 난다.
Try-with-resources
백기선님 피드백에 있는 예제 코드를 사용하겠다.
import java.io.*;
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) in.close( );
if (out != null) out.close( );
}
}
}
이 코드는 src의 파일을 dest으로 복사하는 코드이다. 이 코드에는 문제점이 있다.
만약 finally block에서 in.close();에서 예외가 발생하면 out.close()가 실행되지 않는다. 순서가 바뀌어도 out에서 예외가 발생하면 in을 close할 수 없다. 이것을 처리할 때 finally 문 안에 새롭게 try-catch 문을 작성하면
finally {
if (in !=null) {
try {
in.close( );
} catch (IOException e) {
}
}
if (out != null) {
try {
out.close( );
} catch (IOException e) {
}
}
in.close에서 IOException이 아닌 다른 예를 들면 RuntimeException이 발생하면 마찬가지로 out을 close할 수 없다. 이를 해결하려면 코드는 생략하지만 아래와 같이 복잡한 구조를 사용해야 한다.
try {
try { 본문 }
finally {
try { in.close } } }
finally { try { out.close }}
하지만 try-resource를 사용하면 다음과 같이 코드를 작성할 수 있다.
import java.io.*;
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);
}
}
}
finally에서 close하는 부분은 처리해주지만, catch 블럭까지 없애주는 것은 아니다. throws IOException을 붙였을 뿐이다.
2. 자바가 제공하는 예외 계층 구조, RuntimeException과 RE가 아닌 것의 차이
자바에서는 발생할 수 있는 오류를 클래스로 정의한다. 모든 Exception은 Throwable 클래스의 자손이고 Exception 클래스의 자손이다. 그 중에서 RuntimeException의 클래스를 unchecked exception이라고 부르고 예외처리를 강제하지 않는다. 보통 프로그래머의 실수로 발생하는 예외이다. 개발자의 부주의로 발생하는 경우가 대부분이기 때문에 굳이 로직을 만들어서 처리할 필요가 없다. RuntimeException으로는 ArithmeticException, ClassCastException, NullPointerException, IndexOutOfBoundsException 등이 있다.
그리고 RuntimeException을 제외한 Exception 클래스의 자손을 checked exception이라고 부르고 예외처리를 강제한다. 예시로 IOException, ClassNotFoundException 등이 있다. 일반적으로 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외이다. RuntimeException은 실행 시점에 확인하기 때문에 컴파일에러가 일어나지 않지만, checked exception은 컴파일 시점에 확인해서 예외처리를 강요한다.
Checked exception은 계속 throw를 던져줘야해서 불편할 것 같은데 checked exception을 왜 사용하는 것일까? Runtime 계열은 복구가 어려운 경우에 사용한다. 코딩으로 처리할 수 없는 경우에 사용하는데, 부가적으로 예외를 처리하는 것이 가능할 때 checked exception을 사용하라고 권장하는 것이다.
3. Exception과 Error의 차이는?
Exception과 Error 모두 Throwable class의 자손인데, Error는 OutofMemoryError, StackOverflowError 등 발생하면 복구할 수 없는 심각한 오류이고, 예외는 에러에 비해 대처하기 쉬운 것을 말한다.
4. 커스텀한 예외 만드는 방법
checked exception을 만들고 싶으면 Exception을 상속받으면 되고 unchecked exception을 만들고 싶으면 RuntimeException을 상속받으면 된다. 다음은 읽기 쉬운 커스텀한 예외를 만드는데 지켜야할 4가지 규칙이다.
1. Always Provide a Benifit
기존의 JDK에서 재공하는 exception에 비해 어떤 benifit이 없으면 굳이 custom exception을 만들지 말고 Java에서 제공하는 standard exception을 사용하자.
2. Follow Naming Convention
이름의 끝에 Exception을 붙여서 알아보기 쉽게 만들자. JDK에서 제공하는 exception class의 이름이 Exception으로 끝나는 것을 확인할 수 있다.
3. Provide Javadoc Comments for Your Exception Class
예외의 일반적인 의미와 발생할 수 있는 상황을 Javadoc에 문서화해서 내가 만든 API를 사용하는 다른 개발자들이 error 상황을 피할 수 있게 도와주자.
4. Provide a Constructor That sets the Cause
Custom exception이 발생하기 전에 standard exception을 처리할 때가 있는데 기존 Exception이 에러에 대한 자세한 정보를 포함할 수 있기 때문에 코드에 명시를 해준다. Exception과 RuntimeException은 에러메세지 String과 Throwable 클래스를 인자로 받기 때문에 최소한 하나의 constructor를 만든다.
public class MyBusinessException extends Exception {
public MyBusinessException(String message, Throwable cause, ErrorCode code) {
super(message, cause);
this.code = code;
}
}
출처 : https://dzone.com/articles/implementing-custom-exceptions-in-java?fromrel=true
5. 예외를 사용할 때 장점
1. Seperating Error-Handling code from "Regular" Code
2. Propagating Errors Up the Call Stack
3. Grouping and Differentiating Error Types
출처 : https://docs.oracle.com/javase/tutorial/essential/exceptions/advantages.html
Reference
1. 자바 오라클 튜토리얼
2. 자바의 정석 3판
3. https://dzone.com/articles/implementing-custom-exceptions-in-java?fromrel=true
피드백
1. checked exception을 사용하는 이유를 좀 더 자세하게 적어보자
'자바 > 백기선 자바스터디' 카테고리의 다른 글
[백기선 자바스터디] 제네릭 (0) | 2022.10.19 |
---|---|
[백기선 자바스터디] enum (0) | 2022.10.10 |
[백기선 자바스터디] 인터페이스 (0) | 2022.08.10 |
[백기선 자바스터디] 패키지 (0) | 2022.07.25 |
[백기선 자바스터디] 상속 (0) | 2022.07.14 |