예외처리
1. 프로그램 에러(오류)
1) 프로그램 에러(오류)
발생 시점에 따라 구분 가능하다.
- 컴파일 에러 (컴파일할 때 발생하는 에러)
- 런타임 에러(실행할 때 발생하는 에러)
- 논리적 에러 - 실행은 되지만, 의도와 다르게 동작하는 것
런타임 에러 - 에러(Error) 와 예외(Exception)로 구분 가능
에러(Error) : 프로그램 코드에 의해 수습 불가능한 심각한 오류
ex)메모리부족(OutOfMemoryError), 스택오버플로우(StackOverFlowerError)
예외(Exception) : 프로그램 코드에 의해 수습 가능한 다소 미약한 오류
2) 예외 클래스
자바에서 실행할 때 발생할 수 있는 오류를 클래스로 정의
그림처럼 예외 클래스들은 두 그룹으로 분류 가능하다.
① Exception 클래스와 그 자손들(RuntimeException 클래스와 그 자손들 제외)
② RuntimeException 클래스와 그 자손들
이제부터 RuntimeException 클래스와 그 자손들’을 ‘RuntimeException 클래스들’라고 하며,
RuntimeException 클래스들를 제외한 나머지 클래스들은 ‘Exception 클래스들’ 이라고 할 것이다.
Exception 클래스들 - 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
RuntimeException 클래스들 - 프로그래머의 실수로 발생하는 예외
checked예외 - Exception 클래스들이 발생하면,
문장들에 대한 예외 처리를 하지 않을 시 컴파일조차 되지 않는다.
unchecked예외 - RuntimeException 클래스들이 발생하면,
예외처리를 하지 않더라도 컴파일은 될 것이다. 물론 실행 결과는 비정상적으로 종료될 것이다.
2. 예외 처리하기
(1) 예외 처리하기 try - catch문
예외처리(Exception handling)
정의 - 프로그램 실행시 발생할 수 있는 예외의 발생을 대비해 코드를 작성하는 것
목적 - 프로그램의 비정상적 종료를 막고, 정상적인 실행상태를 유지하는 것
1) 예외 처리하기 위한 try-catch문의 구조
① try 블럭 내에서 예외가 발생한 경우
- 발생한 예외와 일치하는 catch블럭이 있는지 확인한다
- 일치하는 catch블럭을 찾게 되면, 그 catch블럭 내의 문장들을 수행하고
전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다.
일치하는 catch블럭이 없다면 예외는 처리되지 못한다.
② try 블럭 내에서 예외가 발생하지 않은 경우
- catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.
④ 예외의 발생과 catch 블럭
catch 블럭은 괄호()와 블럭 {}으로 나눠져 있다.
괄호()내에는 처리하고자 하는 예외와 같은 타입의 참조변수 하나를 선언해야 한다.
예외가 발생하면, 해당하는 클래스의 인스턴스가 만들어진다.
첫번째 catch블럭부터 차례대로 내려가며 catch블럭의()괄호 내에
선언된 참조변수의 종류와 예외클래스의 인스턴스에 instanceof 연산자를 이용해 검사한다.
검사 결과가 true일때까지 계속된다.
모든 예외 클래스는 Exception 클래스의 자손이므로,
catch블럭의 괄호()에 Exception 클래스 타입의 참조변수를 선언해 놓으면
어떤 종류의 예외가 발생하더라도 이 catch블럭에 의해 처리된다.
2) getMessage()와 printStacktrace()
예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 있다.
getMessage()와 printStacktrace()를 통해서 해당 정보를 얻을 수 있다.
printStacktrace() -예외발생 당시의 호출스택(Call Stack)에 있었던
메서드의 정보와 예외 메시지를 화면에 출력한다
getMessage() - 발생한 예외클래스의 인스턴스에 저장된 메시지를얻을 수 있다.
3) 멀티 catch 블럭
catch 블럭에 ‘|’(논리연산자 아님)기호 이용해서 하나의 블럭으로 합칠 수 있게 되었다.
멀티 catch 블럭이라고한다. 연결할 수 있는 예외 클래스의 개수에는 제한이 없다.
그러나, catch블럭의 ‘|’ 기호로 연결된 예외 클래스가
조상과 자손의 관계라면 컴파일 에러 발생한다.
조상 클래스 하나만 작성하면 되는데, 불필요한 코드로 인해 에러가 발생하는 것이다.
멀티 catch 블럭은 중복된 코드를 줄이는 장점이 있는 반면,
예외를 처리할 때 어떤 예외가 발생한 것인지 알 수 없다는 단점이 있다.
따라서 참조변수 e로 멀티 catch블럭에 ‘|’로 연결된 예외 클래스들의
공통 분모인 조상 예외 클래스에 선언된 멤버만 사용할 수 있다.
4) 예외 발생 시키기
예외 발생 시키기 - 키워드 throw 사용
①연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든 뒤
Exception e = new Exception(“고의로 발생시켰음”);
②키워드 throw를 이용해 예외를 발생시킨다
throw e;
Exception 인스턴스 생성할 때, 생성자에 String을 넣어주면
이 String이 Exception인스턴스에 메시지로 저장된다.
이 메시지는 getMessage()를 이용해 얻을 수 있다.
(2) 예외 처리하기 - 메서드에 예외 선언하기
메서드에 예외 선언하기 방법
-메서드 선언부에 키워드 throws를 사용해서
메서드 내에서 발생할 수 있는 예외를 적어주면 된다.
만약 예외가 여러개라면 쉼포(,)를 통해 구분한다.
예외가 선언되어 있으면 Exception 같은 checked 예외를
try-catch문으로 처리하지 않더라도 컴파일 에러가 발생하지 않는다. (컴파일은 된다.)
만약 위와 같이 모든 예외의 최고조상인 Exception클래스를 메서드에 선언한다면,
이 메서드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다.
메서드의 선언부에 예외를 선언함으로써
이 메서드를 사용하려는 사람이 메서드의 선언부를 보았을 때
이 메서드를 사용하기 위해서는 어떠한 예외들이 처리돼야 하는지를 쉽게 알 수 있다.
위를 통해 다음과 같은 사실을 알 수 있다.
- 예외가 발생했을 때, 모두 3개의 메서드 (main, method1, method2)가 호출스택에 있었으며
- 예외가 발생한 곳은 제일 윗줄에 있는 method2()라는 것과
- main 메서드가 method1()을, method1()이 method2()를 호출했다는 것을 알 수 있다.
method2()에서 ‘throw new Exception();’ 문장에 의해 강제로 예외가 발생했다.
그러나 try-catch문으로 예외처리 하지 않았으므로
main메서드가 종료되어 프로그램이 예외로 인해 비정상적으로 끝나게 된다.
이처럼 예외가 발생한 메서드에서 예외처리를 하지 않고
자신을 호출한 메서드에게 예외를 넘겨줄 수는 있지만,
예외가 처리된 것은 아니므로 try-catch문으로 예외처리를 해줘야 한다.
파일을 생성하는 것은 createFile()인데,
이 메서드는 입력받은 파일의 이름이 유효하지 않으면 예외를 발생시킨다.
createFile()에 예외가 선언되었으므로, 이 예외는 main()으로 전달 되고
main()의 try-catch문에 의해 처리 된다.
cf. 예외 되던지기
method1()과 main()메서드 양쪽의 catch블럭이 모두 수행됐음을 알 수 있다.
method1()의 catch블럭에서 예외를 처리하고도 throw문을 통해 다시 예외를 발생시켰다.
그리고 이 예외를 main메서드가 처리한 것이다.
(3) finally 블럭
finally블럭은 예외의 발생여부와 상관없이
실행해야할 코드를 포함시킬 목적으로 사용된다.
try-catch문의 끝에 선택적으로 덧붙여 사용할 수 있으며, try-catch -finally의 순서로 구성된다.
예외가 발생한 경우, try -> catch -> finally 순으로 실행되고,
예외가 발생하지 않은 경우에는 try -finally 순으로 실행된다.
위는 프로그램 설치할 때 예외의 발생 여부와 무관하게
임시파일을 삭제해야하는 상황이다. finally 블럭에 넣는 게 훨씬 낫다.
(4) 연결된 예외(chained exception)
연결된 예외를 사용하는 이유
① 하나의 큰 부류의 예외로 묶어서 다루기 위해서다.
② checked 예외를 unchecked 예외로 바꾸기 위해서다.
initCause(Throwable cause) - 지정한 예외를 원인 예외로 등록
getcause() - 원인 예외를 반환
MemoryException은 Exception의 자손이므로 반드시 예외처리를 해야하는데,
이 예외를 RuntimeException으로 감싸버렸기 때문에 unchecked예외가 되었다.
그래서 더이상 startInstall()의 선언부에 MemoryException을 선언하지 않아도 된다.
참고로 위의 코드에서는 InitCause() 대신 RuntimeException의 생성자를 사용했다.
'Java' 카테고리의 다른 글
Java 날짜와 시간 & 형식화 (0) | 2022.04.18 |
---|---|
java.lang패키지와 유용한 클래스-(1) (0) | 2022.04.15 |
re : Java 객체지향 프로그래밍Ⅱ (0) | 2022.04.13 |
re: Java 객체지향 프로그래밍Ⅰ (0) | 2022.04.12 |
Java 배열 (0) | 2022.04.11 |