'자바의 정석 3rd Edition'을 공부한 후 정리한 내용입니다.
1.1 프로그램 오류
- 프로그램 에러(오류): 프로그램이 실행 중 어떤 원인에 의해서 오작동하거나 비정상적으로 종료되는 경우, 이 결과를 초래하는 원인
컴파일 에러 컴파일 시에 발생하는 에러
런타임 에러 실행 시에 발생하는 에러
논리적 에러 실행은 되지만, 의도와 다르게 동장하는 것
에러(error) 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- ex) 메모리 부족, 스택오버플로우
예외(exception) 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
- 에러가 발생하면, 프로그램의 비정상적인 종료를 막을 길이 없지만, 예외는 발생하더라도 프로그래머가 이에 대한 적절한 코드를 미리 작성해 놓음으로써 프로그램의 비정상적인 종료를 막을 수 있음
1.2 예외 클래스의 계층구조
자바에서는 실행 시 발생할 수 있는 오류(exception, error)를 클래스로 정의
모든 예외의 최고 조상은 Exception 클래스
사진 출처: https://velog.io/@orpsh1941/JAVA%EC%9D%98-%EC%A0%95%EC%84%9D%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC
예외 클래스는 두 급룹으로 나눠짐
- Exception 클래스와 자손들
- 주로 외부의 영향으로 발생. 프로그램의 사용자들의 동작에 의해서 발생
- ex) 존재하지 않는 파일의 이름을 입력, 실수로 클래스의 이름을 잘못 적음, 입력한 데이터 형식이 잘못된 경우
- RuntimeException 클래스와 자손들
- 주로 프로그래머의 실수에 의해 발생될 수 있는 에러들로 자바의 프로그래밍 요소들과 관계가 깊음
- ex) 배열의 범위를 벗어남, 값이 null인 참조변수의 멤버를 호출, 클래스간의 형변환을 잘못함, 정수를 0을 나누려는 경우
- Exception 클래스와 자손들
1.3 예외처리하기 - try-catch문
예외처리(exception handling)
- 정의: 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것
- 목적: 예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행 상태를 유지할 수 있도록 하는 것
발생한 예외를 처리하지 못하면 프로그램은 비정상적으로 종료되며, 처리되지 못한 예외는 JVM의 '예외 처리기'가 받아서 예외의 원인을 화면에 출력
예외 처리를 위해서는 try-catch문을 사용
try { // 예외가 발생할 가능성이 있는 문장을 넣음 } catch (Exception1 e1) { // Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 적음 } catch (Exception2 e2) { // Exception2이 발생했을 경우, 이를 처리하기 위한 문장을 적음 } catch (Exception3 e3) { // Exception3이 발생했을 경우, 이를 처리하기 위한 문장을 적음 }
하나의 try 블럭 다음에는 여러 종류의 예외를 처리할 수 있도록 하나 이상의 catch 블럭을 받아올 수 있음
- 발생한 예외의 종류와 일치하는 단 한 개의 catch 블럭만 수행됨
- 발생한 예외의 종류와 일치하는 catch 블럭이 없으면 예외는 처리되지 않음
1.4 try-catch문에서의 흐름
try 블럭 내에서 예외가 발생한 경우
- 발생한 예외와 일치하는 catch 블럭이 있는지 확인
- 일치하는 catch 블럭을 찾게 되면, catch 블럭 내의 문장들을 수행하고 전체 try-catch문을 빠져나가 그 다음 문장을 계속해서 수행. 만일 일치하는 catch 블럭을 찾지 못하면, 예외는 처리되지 못함
- try 블럭에서 예외가 발생하면, 예외가 발생한 위치 이후에 있는 try 블럭의 문장들은 수행되지 않음
try 블럭 내에서 예외가 발생하지 않는 경우
- catch 블럭을 거치지 않고 전체 try-catch문을 빠져나가 수행을 계속
1.5 예외의 발생과 catch블럭
- catch 블럭은 괄호()와 블럭{} 두 부분으로 나눠져 있는데, ()내에는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언
- 예외가 발생한 문장이 try 블럭에 포함되어 있다면, 이 예외를 처리할 수 있는 catch 블럭이 있는지 찾게됨
- 첫 번째 catch 블럭부터 차례로 내려가면서 catch 블럭의 괄호 ()내에 선언된 참조변수의 종류와 생성된 예외 클래스의 인스턴스에 instanceof 연산자를 이용해서 검사하게 되는데, 검사결과가 true인 catch 블럭을 만날 때까지 검사는 계속됨
- 검사 결과가 true인 catch 블럭을 찾게 되면 블럭에 있는 문장들을 모두 수행한 후에 try-catch 문을 빠져나가고 예외는 처리되지만, 검사결과가 true인 catch 블럭이 하나도 없으면 예외는 처리되지 않음
- 모든 예외 클래스는 Exception 클래스의 자손이므로, catch 블럭의 괄호()에 Exception 클래스 타입의 참조변수를 선언해 놓으면 어떤 종류의 예외가 발생하더라도 이 catch 블럭에 의해 처리됨
printStackTrace()와 getMessage()
printStackTrace() 예외발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력
getMessage() 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있음
- catch 블럭의 ()에 선언된 참조변수를 통해 이 인스턴스에 접근 가능
- 이 참조변수로 선언된 catch 블럭 내에서만 사용 가능
멀티 catch블럭
- JDK 1.7부터 여러 catch 블럭을 '|'기호를 이용해, 하나의 catch 블럭으로 합칠 수 있게 됨
- 멀티 catch 블럭을 사용하면 중복된 코드를 줄일 수 있음
- '|' 기호로 연결할 수 있는 예외 클래스의 개수에는 제한이 없음
- 멀티 catch는 하나의 catch 블럭으로 여러 예외를 처리하는 것이기 때문에, 발생한 예외를 멀티 catch 블럭으로 처리하게 될 경우 실제 어떤 예외가 발생한 것인지 알 수 없음
- 참조변수 e로 멀티 catch 블럭에 '|' 기호로 연결된 예외 클래스의 공통 분모인 조상 예외 클래스에 선언된 멤버만 사용할 수 있음
- 멀티 catch 블럭에 선언된 참조변수 e는 상수이므로 값을 변경할 수 없음. 여러 catch 블럭이 하나의 참조변수를 공유하기 때문
1.6 예외 발생시키기
- 연산자 new를 이용해서 발생시키려는 예외의 클래스 객체를 만듦
Exception e = new Exception("고의로 발생시켰음");
- 키워드 throw를 이요해서 예외를 발생시킴
throw e;
Exception 인스턴스를 생성할 때, 생성자로 String을 넣어주면, 이 String이 Exception 인스턴스에 메시지로 저장됨
- 이 메시지는
getMessage()
를 이용해서 얻을 수 있음
- 이 메시지는
Exception 클래스들이 발생할 가능성이 있는 문장들에 대해 예외처리를 해주지 않으면 컴파일조차 되지 않음
RuntimeException 클래스들에 해당하는 예외는 프로그래메에 의해 실수로 발생하는 것들이기 때문에 예외처리를 강제하지 않음
- RuntimeException 클래스들에 속하는 예외가 발생할 가능성이 있는 코드에도 예외처리를 필수로 해야 한다면, 참조 변수와 배열이 사용되는 모든 곳에 예외처리를 해주어야 함
컴파일러가 예외처리를 확인하지 않는 RuntimeException 클래스들은 'unchecked 예외'라고 부르고, 예외처리를 확인하는 Exception 클래스들은 'checked 예외'라고 부름
1.7 메서드에 예외 선언하기
- 예외를 처리하는 방법은 try-catch문을 사용하는 것 외에, 예외를 메서드에 선언하는 방버비 있음
void method() throws Exception1, Exception2, ..., ExceptionH { // 메서드의 내용 }
- 메서드에 예외를 선언하려면, 메서드의 선언부에 키워드
throws
를 사용해 메서드 내에서 발생할 수 있는 예외를 적어줌- 예외가 여러 개일 경우 쉼표로 구분
throw
(예외를 발새시키는 키워드)와 구분
- 메서드의 선언부에 예외를 선언함으로써 메서드를 사용하려는 사람이 메서드의 선언부를 보았을 때, 메서드를 사용하기 위해서는 어떠한 예외들이 처리되어져야 하는지 쉽게 알 수 있음
- 메서드에 예외를 선언할 때 RuntimeException클래스들은 적지 않음
throws
에는 반드시 처리해주어야 하는 예외들만 선언
- 예외가 발생한 메서드에서 예외처리를 하지 않고 자신을 호출한 메서드에게 예외를 넘겨줄 수 있지만, 이것은 예외가 처리된 것이 아니라 단순히 전달만 하는 것
- 어느 한 곳에서는 반드시
try-catch
문으로 예외를 처리해줘야 함 - 예외가 발생한 메서드 내에서 자체적으로 처리해도 되는것은 메서드 내에서
try-catch
문을 사용. 메서드에 호출 시 넘겨받아야 할 겂을 다시 받아야 하는 경우(메서드 내에서 자체적으로 해결이 안 되는 경우)에는 예외를 메서드에 선언해서, 호출한 메서드에서 처리해야 함
- 어느 한 곳에서는 반드시
1.8 finally 블럭
try {
// 예외가 발생할 가능성이 있는 문장
} catch (Exception1 e1) {
// 예외처리를 위한 문장
} finally {
// 예외의 발생여부에 관계없이 항상 수행되어야 할 문장
// finally 블럭은 try-catch문의 맨 마지막에 위치해야함
}
- 예외가 발생한 경우: try -> catch -> finally
- 예외가 발생하지 않은 경우: try -> finally
- try, catch 블럭이 실행되는 도중 return문이 실행되더라도 finally 블럭의 문장들이 먼저 실행된 후에 현재 실행 중인 메서드를 종료
1.9 자동 자원 반환 - try-with-resources문
- JDK1.7부터 추가됨
- 입출력과 관련되 클래스를 사용할 때 유용
try-with-resource
문의 괄호()안에 객체를 생성하는 문장을 넣으면, 이 객체는 따로close()
를 호출하지 않아도 try 블럭을 벗어나는 순간 자동적으로close()
가 호출됨. 그 다음 catch 블럭 또는 finally 블럭이 실행됨try-with-resource
문에 의해 자동으로 객체의close()
가 호출될 수 있으려면, 클래스가AutoCloseable
이라는 인터페이스를 구현한 것이어야 함
예시
class TryWithResourceEx {
public static void main(String args[]) {
try (CloseableResource cr = new CloseableResource()) {
cr.exceptionWork(false); // 예외가 발생하지 않는다.
} catch(WorkException e) {
e.printStackTrace();
} catch(CloseException e) {
e.printStackTrace();
}
System.out.println();
try (CloseableResource cr = new CloseableResource()) {
cr.exceptionWork(true); // 예외가 발생한다.
} catch(WorkException e) {
e.printStackTrace();
} catch(CloseException e) {
e.printStackTrace();
}
} // main의 끝
}
class CloseableResource implements AutoCloseable {
public void exceptionWork(boolean exception) throws WorkException {
System.out.println("exceptionWork("+exception+")가 호출됨");
if(exception)
throw new WorkException("WorkException발생!!!");
}
public void close() throws CloseException {
System.out.println("close()가 호출됨");
throw new CloseException("CloseException발생!!!");
}
}
class WorkException extends Exception {
WorkException(String msg) { super(msg); }
}
class CloseException extends Exception {
CloseException(String msg) { super(msg); }
}
/*결과*/
exceptionWork(false)가 호출됨
close()가 호출됨
CloseException: CloseException발생!!!
at CloseableResource.close(TryWithResourceEx.java:33)
at TryWithResourceEx.main(TryWithResourceEx.java:6)
exceptionWork(true)가 호출됨
close()가 호출됨
WorkException: WorkException발생!!!
at CloseableResource.exceptionWork(TryWithResourceEx.java:28)
at TryWithResourceEx.main(TryWithResourceEx.java:14)
Suppressed: CloseException: CloseException발생!!!
at CloseableResource.close(TryWithResourceEx.java:33)
at TryWithResourceEx.main(TryWithResourceEx.java:15)
main
메서드- 두 개의
try-catch
문 중 첫 번째는 close()에서만 예외를 발생시키고, 두 번째는 exceptionWork()와 close()에서 모두 예외를 발생시킴
- 두 개의
- 첫 번째는 일반적인 예외가 발생했을 때와 같은 형태로 출력되지만, 두 번째는 출력 형태가 다름
- 두 예외가 동시에 발생할 수 없기 때문에, 실제 발생한 예외를 WorkException으로 하고, CloseException은 억제된(Suppressed) 예외로 다룸.
- 억제된 예외에 대한 정보는 실제로 발생한 예외인 WorkException에 저장됨
- 기존의
try-catch
문에서는finally
블럭에try-catch
문을 추가해 예외를 처리해줄 수 있지만, 만약try
블럭과finally
블럭에서 모두 예외가 발생하면try
블럭의 예외는 무시됨
1.10 사용자정의 예외 만들기
- Exception 클래스 또는 RuntimeException 클래스로부터 상속받아 클래스를 만들지만, 필요에 따라 알맞은 예외 클래스 선택이 가능
1.11 예외 되던지기(exception re-throwing)
한 메서드에서 발생할 수 있는 예ㅚ가 여럿인 경우, 몇 개는 try-catch문을 통해 메서드 내에서 자체적으로 처리하고, 나머지는 선언부에 지정하여 호출한 메서드에서 처리하도록 함으로써, 양쪽에 나눠서 처리되도록할 수 있음
예외 되던지기: 예외를 처리한 후에 인위적으로 다시 발생시키는 방법
- 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해줌
- catch문에서 필요한 작업을 행한 후 throw문을 사용해서 예외를 다시 발생시킴
- 다시 발생한 예외는 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 또 다시 처리
하나의 예외에 대해 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용
- 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를
throws
에 지정해줘야 함
- 예외가 발생할 메서드에서는 try-catch문을 사용해서 예외처리를 해줌과 동시에 메서드의 선언부에 발생할 예외를
반환값이 있는 return문의 경우 catch블럭에도 return문이 있어야 함. 예외가 발생했을 경우에도 값을 반환해야하기 때문
- catch 블럭에서 예외 되던지기를 해서 호출한 메서드로 예외를 전달하면, return문은 없어도 됨
1.12 연결된 예외(chained exception)
- 한 예외가 다른 예외를 발생시킬 수 있음
- 예외 A가 예외 B를 발생시켰다면, A를 B의 '원인 예외(cause exception)'이라고 함
Throwable initCause(Throwable cause) 지정한 예외를 원인 예외로 등록
Throwable getCause() 원인 예외를 반환
발생한 예외를 원인 예외로 등록한 후 다시 예외를 발생시키는 이유
- 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함
- checked 예외를 unchecked 예외로 바꿀 수 있게 하기 위함
- checked 예외로 예외처리를 강제한 이유는 프로그래밍 경험이 적은 사람도 견고한 프로그램을 작성할 수 있도록 유도하기 위한 것
- 현재는 컴퓨터 환경이 많이 달라져, checked 예외가 발생해도 예외를 처리할 수 없는 상황이 발생하기 시작
- 이럴 때 의미없는 try-catch문을 추가할 필요없이 checked 예외를 unchecked 예외로 바꾸면 예외처리가 선택적이 되므로 억지로 예외 처리를 하지 않아도 됨
Chapter 8 끝!!!
'Book > Java의 정석' 카테고리의 다른 글
[Chapter 9] java.lang 패키지와 유용한 클래스 (0) | 2022.04.01 |
---|---|
[Chapter 7] 객체지향 프로그래밍 2 (0) | 2022.04.01 |
[Chapter 6] 객체지향 프로그래밍 1_2 (0) | 2022.04.01 |
[Chapter 6] 객체지향 프로그래밍 1_1 (0) | 2022.04.01 |
[Chapter 5] 배열 (Array) (0) | 2022.04.01 |
댓글