목표
자바의 예외 처리에 대해 학습하세요.
학습할 것 (필수)
- 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
- 자바가 제공하는 예외 계층 구조
- Exception과 Error의 차이는?
- RuntimeException과 RE가 아닌 것의 차이는?
- 커스텀한 예외 만드는 방법
마감일시
2021년 1월 16일 토요일 오후 1시까지.
자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
📌 예외처리란?
프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것이며, 예외처리의 목적은 예외의 발생으로 인한 실행 중인 프로그램의 갑작스러운 비정상적인 종료를 막고, 정상적인 실행 상태를 유지할 수 있도록 대비, 예방하는 것이다. (by 자바의 정석)
try-catch의 기본 구조는 아래와 같다
try 블럭에서는 동작할 수 있는 코드를 작성하며 catch블록에서는 발생할 수 있는 예외에 대한 처리 문장을 넣는다.
위 그림과 같이 여러 종류의 예외를 처리할 수 있도록 catch블록이 다중으로 사용 가능하며 없으면 예외처리되지 않는다. 또한 try블록 안에 try-catch가 들어가도 되며 예외가 발생하지 않는다면 try-catch블록을 빠져나가 다음 문장을 실행한다.
아래(자바가 제공하는 예외 계층 구조)에서 보듯이 모든 예외 클래스는 Exception클래스의 자손이므로, catch블럭의 괄호()에 Exception클래스 타입의 참조 변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 이 catch 블럭에 처리된다.
자바에서는 첫번째 일치하는 catch블럭만 처리하기 때문에 위와 같이 IllegalArgumentException이 처리되면 그다음 catch블럭인 Exception은 처리되지 않는다.
에러 메시지 toString() vs printStackTrace() vs getMesage()
package choi.hyang.study.chapter9;
public class exceptionStudy {
public static void main(String[] args) {
App c1 = new App();
c1.setOprands(10, 0);
c1.divide();
}
}
class App {
int left, right;
public void setOprands(int left, int right){
this.left = left;
this.right = right;
}
public void divide(){
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch(Exception e){
System.out.println("\n\ne.getMessage()\n"+e.getMessage());
System.out.println("\n\ne.toString()\n"+e.toString());
System.out.println("\n\ne.printStackTrace()");
e.printStackTrace();
}
}
}
e.getMesage() : 발생한 예외 클래스의 인스턴스에 저장된 메시지를 읽을 수 있고 오류에 대한 기본적인 내용을 출력해준다. 상세하진 않다.
e.toString() : e.toString()은 e.getMessage()보다 더 자세한 예외 정보를 제공한다.
e.printStackTrace() : getMesage()와 toString()과는 다르게 printStackTrace는 리턴 값이 없다. 이 메소드를 호출하게 되면 예외 발생 당시의 호출 스택(Call stack)에 있던 메소드의 정보와 예외 결과를 화면에 출력한다.
멀티 catch블럭
자바 7부터 여러 catch블럭을 '|' 기호를 이용하여 하나의 catch블럭으로 합칠 수 있게 되었다.
멀티 catch블럭을 사용하게 되면 불필요한 코드 중복을 줄일 수 있다.
위의 코드를 가져와서 catch블록에 예외를 추가하면 아래와 같이 추가할 수 있다.
try {
System.out.print("계산결과는 ");
System.out.print(this.left/this.right);
System.out.print(" 입니다.");
} catch( IllegalArgumentException | IndexOutOfBoundsException e ) {
e.printStackTrace();
}
주의해야 될 사항이 있다.
catch블럭에 (자손예외 | 조상예외 e) 과 (조상예외 e)는 같은 코드이므로 왼쪽 코드에서는 컴파일 오류가 발생한다.
CheckedException vs UnCheckedException
컴파일러가 예외처리를 확인하지 않는 RuntimeException클래스들은 uncheckedException
예외처리를 확인하는 Exception클래스들은 checkedException라고 부른다.
즉, checkedException은 컴파일 단계에서 확인되며 uncheckedException은 컴파일 단계에서 확인되지 않는다. checkedException은 컴파일 시점에서 확인되므로 반드시 예외처리를 해주어야 하며 예외처리를 하지 않을 경우 컴파일 에러가 발생한다.
checkedException은 try-catch나 throws로 예외를 처리한다.
위 코드에서 tempList.get(3)을 하게 되면 IndexOutOfBoundsException이 발생하지만
try-catch 블록으로 감싸지 않아도 컴파일 시점에서는 에러가 발생하지 않기 때문에 unchecked예외에 해당한다.
finally
finally는 try-catch 구문에서 예외가 발생하는 것과 상관없이 언제나 실행되는 로직이다.
예외가 발생한 경우 : try -> catch -> finally 순서로 동작
예외가 발생하지 않은 경우 : try -> finally 순서로 동작
try {
// 예외가 발생할 수 있는 로직 장성
} catch(Exception e) {
// 예외 처리
} finally {
// 예외 발생 여부와 상관없이 항상 수행되어야 하는 문장
// finally 블럭은 try-catch 다음에 위치한다
// 리소스를 정리하는 코드를 주로 씀
}
try-with-resources(자바7 이후)
아래 코드는 파일로부터 데이터를 읽는 코드인데 예외가 발생하더라도 Stream이 닫힐 수 있게 finally에서 close를 하고 있다. 우선 아래 코드의 문제점은 코드가 너무 복잡하며, try블럭에서의 예외와 finally블럭에서 예외가 두번 발생하면 try블럭의 예외는 무시가 된다. 이러한 점을 개선하기 위해 try-with-resources가 등장했다.
public class TryWithResourceExam {
public static void main(String[] args) {
FileInputStream fis;
DataInput dis;
try {
fis = new FileInputStream("test.txt");
dis = new DataInputStream(fis);
// ... 로직
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (dis != null) {
dis.close();
}
} catch (IOException ie) {
ie.printStackTrace();
}
}
}
}
try-with-resource는 try블럭에 복수의 자원 객체를 전달하면, 자원들이 실행 종료 후 자동으로 close 해주는 예외 처리 방식이다. try에 전달할 수 있는 자원은 AutoCloseable 인터페이스를 구현한 클래스여야 된다.
try(Something1 s1 = new Something1();
Something2 s2 = new Something2()) {
} catch(...) {
...
}
AutoCloseable.interface
public interface AutoCloseable {
void close() throws Exception;
}
위 코드를 try-with-resources로 개선한다면 아래와 같이 바꿀 수 있다.
public class TryWithResourceExam {
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("test.txt");
DataInputStream dis = new DataInputStream(fis)) {
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
예외 떠넘기기 throws
예외를 떠넘긴다는 말은 예외가 발생했을 경우 자기 자신이 처리하지 않고 이 메소드를 호출하는 쪽에 책임을 전가하겠다는 뜻이다.
그렇다면 throw는?
throw는 사용자가 직업 예외를 만들어서 일부로 발생시킬 때 사용하는 예약어이다.
예를 들면 아래 코드에서 makeException메소드는 IllegalArgumentException를 무조건 호출하는 메서드이다.
public void makeException() {
Exception e = new Exception("강제로 에러발생");
throw e;
throw new Exception("강제로 에러발생"); // 위 두 줄의 코드를 한번에
}
자바가 제공하는 예외 계층 구조
java.lang패키지에 있는 자바 예외 계층 구조
java.lang패키지에 있는 자바 에러 계층 구조
Exception과 Error의 차이는?
오류는 컴파일 에러, 런타임 에러, 논리적 에러 이 3가지로 구별할 수 있으며
그중 자바에서는 런타임 시 발생할 수 있는 프로그램 오류를 에러와 예외 2가지로 구분한다.
에러는 메모리 부족(OutOfMemoryError)이나 스택 오버 플로우(StackOverflow)와 같이 일단 발생하면 복구할 수 없는 심각한 오류이다. 에러가 발생하면 비정상적인 종료를 막을 수 없다.
예외는 발생하더라도 수습될 수 있는 비교적 덜 심각한 오류를 말한다. 예외는 발생하더라도 프로그래머가 적절한 코드르 통해 비정상적인 종료를 막을 수 있다.
RuntimeException과 RE가 아닌 것의 차이는?
자바에서 RuntimeException은 Unchecked Exception으로 RuntimeException이 아닌 것은 Checked Exception으로 분류됩니다.
구분 | Checked Exception | Unchecked Exception |
확인 시점 | 컴파일(Compile) 시점 | 런타임 시점(Runtime) 시점 |
처리 여부 | 반드시 예외 처리해야 한다. | 명시적으로 하지 않아도 된다. |
종 류 | IOException, ClassNotFoundException 등 | NullPointerException, ClassCastException 등 |
(표 참고 )
커스텀한 예외 만드는 방법(=사용자 정의 예외)
프로그래머가 새로운 예외 클래스를 정의할 수 있다. 보통 Exception클래스를 상속받는 클래스를 만들지만, 필요에 따라 알맞은 예외 클래스를 상속받아서 커스텀한 예외를 만들 수도 있다.
커스텀한 예외를 만드는 코드이다.
(위와 같이 매개변수의 String을 받아야 메시지를 볼 수 있다.)
public class CustomMyException extends Exception{
public CustomMyException(String message) {
super(message);
}
public static void main(String[] args) throws CustomMyException {
throw new CustomMyException("내가 만든 예외 강제 발생");
}
}
Reference
자바의 정석
'Java' 카테고리의 다른 글
8주자 과제: 인터페이스(피드백) (0) | 2021.01.28 |
---|---|
9주차 과제: 예외 처리(피드백) (0) | 2021.01.17 |
8주자 과제: 인터페이스 (0) | 2021.01.09 |
7주차 과제: 패키지 (피드백, feedback) (0) | 2021.01.02 |
7주차 과제: 패키지 (0) | 2021.01.01 |