예외처리
- 에러/오류 : 프로그램 실행 중 어떤 원인에 의해서 오작동을 하건 비정상적으로 종료가 되는 경우
- 컴파일 에러 : 컴파일할때 발생하는 에러
- 런타임 에러 : 프로그램 실행 도중 발생하는 에러
- 논리적 에러 : 컴파일도 잘 되고 실행도 잘 되지만 의도한 것과 다르게 동작하는 것
- 자바에서는 실행시 발생할 수 있는 프로그램 오류(런타임 에러)를 에러, 예외 두가지로 구분하였음
- 에러(Error) : 프로그램 코드에 으해서 수습될 수 없는 심각한 오류 ex) OutOfMemory / StackOverflow
- 예외(Exception): 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
- 자바에서는 실행 시 발생할 수 있는 오류(Exception과 Error)를 클래스로 정의하였음
- 모든 클래스의 조상은 Object이므로 Exception과 Error클래스 역시 Object클래스의 자손임
- Exception클래스는 두 그룹으로 나눠짐.
1. RuntimeException클래스와 그 자손들
2. Exception클래스와 그 자손들
- Exeption클래스들은 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
- RuntimeException클래스는 프로그래머의 실수로 발생하는 예외
- 예외는 프로그래머가 이에 대한 처리를 미리해주어야 함
- 예외처리란, 프로그램 실행 시 발생할 수 있는 예기치 못한 예외의 발생에 대비한 코드를 작성하는 것으로 프로그램의 비정상적인 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는 것.
- 발생한 예외를 처리하지 못하면 프로그램은 비정상적으로 종료되고, 처리되지 못한 예외는 JVM의 예외처리기가 받아서 예외의 원인을 화면에 출력한다.
try{
// 예외가 발생할 가능성이 있는 문장들을 넣는다
} catch(Exception1 e1){
// Exception1이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch(Exception2 e2){
// Exception2가 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch(ExceptionN eN){
// ExceptionN이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}
public class ExceptionEx2 {
public static void main(String[] args) {
int number = 100;
int result = 0;
for(int i =0; i<10; i++){
result = number / (int)(Math.random() * 10);
System.out.println(result);
}
}
}
- 정수는 0으로 나누는 것이 금지되어있기 때문에
이렇게 ArithmeticException이 뜨게 된다.
이제 여기서 예외처리구문을 추가하면,
package ch8;
public class ExceptionEx2 {
public static void main(String[] args) {
int number = 100;
int result = 0;
for(int i =0; i<10; i++){
try {
result = number / (int) (Math.random() * 10);
System.out.println(result);
}catch(ArithmeticException e){
System.out.println("0");
}
}
}
}
이렇게 ArithmeticException에 해당하는 catch블럭을 찾아서 그 catch블럭내의 문장들을 실행하여 catch블럭내의 문장을 실행하고, try-catch문을 벗어나 for문의 다음 반복을 마치고 정상적으로 종료된다.
public class ExceptionEx7 {
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try{
System.out.println(3);
System.out.println(0/0);
System.out.println(4); // 실행되지 않음
}catch(ArithmeticException ae){
if(ae instanceof ArithmeticException)
System.out.println("true");
}catch (Exception e){
System.out.println("Exception");
} // try-catch끝
System.out.println(6);
}
}
- try블록에서 ArithmeticException이 발생하여 catch블록을 하나씩 차례대로 검사하고, 첫 번째 검사에서 일치하는 catch블럭을 찾았기 때문에 두 번재 catch블록은 검사하지 않게 된다.
- printStackTrace() : 예외 발생 당시의 호출 스택에 있었던 메서드의 정보와 예외메시지를 화면에 출력한다.
- getMessage() : 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
- 멀티 catch 블럭 : JDK 1.7.부터 여러 catch블럭을 '|' 기호를 이용해서, 하나의 catch블록으로 합칠 수 있게 하였다. 다만 해당 예외 클래스가 조상과 자손 관계에 있다면 컴파일 에러가 발생한다.-> 불필요한 코드는 제거하라는 의미에서 컴파일 에러가 발생함
예외 발생시키기
- 키워드 throw를 사용해서 프로그래머가 고의로 예외를 발생시킬 수 있다.
1. 연산자 new를 이용해서 발생시키려는 예외 클래스의 객체를 만든다.
2. 키워드 throw를 이용해서 예외를 발생시킨다.
public class Exception9 {
public static void main(String[] args) {
try{
Exception e = new Exception("고의로 발생시켰음");
throw e; // 예외로 발생시킴
// throw new Exception("고의로 발생시켰음."); -> 이렇게 한줄로 축약해서 써도 된다.
}catch (Exception e){
System.out.println("에러 메시지: " + e.getMessage() );
e.printStackTrace();
}
System.out.println("프로그램이 정상 종료되었음");
}
}
public class Exception10 {
public static void main(String[] args) {
throw new Exception(); // 컴파일 에러
}
}
- Exception을 고의로 발생시키면 바로 컴파일 에러가 나는데, 예외 처리가 되어야 할 부분에 예외처리가 되어있지 않기 때문에 나타난다. 반면
public class Exception10 {
public static void main(String[] args) {
throw new RuntimeException();
}
}
이 코드는 예외처리를 하지 않았지만 성공적으로 컴파일 된다. 하지만 실행해보면,
위의 실행 결과처럼 RuntimeException이 발생하여 비정상적으로 종료 된다.
이렇게 컴파일 오류가 뜨지 않는 이유는, RuntimeException과 그 자손들은 프로그래머의 실수에 의해 발생하는 것이기 때문에 예외처리를 강제당하지 않는다는 것이다. 컴파일러가 예외처리를 확인하지 않는 RuntimeException클래스들은 'unchecked Exception'이라 하고, 예외처리를 확인하는 클래스들은 'Checked Exception'이라 부른다.
package ch8;
public class Exception12 {
public static void main(String[] args) throws Exception{
method1();
}
static void method1() throws Exception{
method2();
}
static void method2() throws Exception{
throw new Exception();
}
}
위의 예제에서 예외가 발생한 곳은 method2()에서 발생하였지만 try-catch문으로 예외처리를 해주지 않았으므로 자신을 호출한 method1()에게 예외를 넘겨준다. method1()에서도 역시 예외처리를 하지 않았으므로 종료되면서 main메서드에게 예외를 넘겨준더다. 그러나 main에서 조차 try-catch로 예외처리를 해주지 않았으므로 main메서드가 종료되어 프로그램이 예외로 인해 비정상적으로 종료되어버린다.
따라서 이러한 사태를 방지하기 위해 어느 곳에서는 반드시 try-catch로 예외처리를 해주어야 한다.
finally
- finally 블럭 : finally 블럭은 예외의 발생 여부에 상관 없이 실행되어야 할 코드를 포함시킬 목적으로 사용된다. try-catch문의 끝에 선택적으로 덧붙여 사용할 수 있으며, try-catch-finally의 순서로 구성된다.
try{
// 예외가 발생할 가능성이 있는 문장을 넣는다
}catch(Exception1 e1){
// 예외처리를 위한 문장을 넣는다
}finally{
// 예외의 발생여부에 관계없이 항상 수행되어야하는 문장들을 넣는다
// finally블럭은 try-catch문의 맨 마지막에 위치해있어야 한다.
}
- try나 catch블럭에서 return문이 실행되는 경우에도 finally블럭의 문장들이 먼저 실행된 후에, 실행중인 메서드를 종료한다. 무조건 실행하는 흐름.
public class FinallyTest3 {
public static void main(String[] args) {
FinallyTest3.method1();
System.out.println("method1()의 수행을 마치고 main메서드로 돌아왔습니다.");
}
static void method1(){
try{
System.out.println("method1()이 호출되었습니다.");
return;
}catch (Exception e){
e.printStackTrace();
} finally {
System.out.println("method1()의 finally블럭이 실행되었습니다.");
}
}
}
try-with-resource
- try-with-resources문의 괄호()안에 객체를 생성하는 문장을 넣으면, 이 객체는 close()를 따로 호출하지 않아도 try블록을 벗어나는 순간 자동으로 close()가 호출된다. 이렇게 자동으로 객체의 close()가 호출되려면, 클래스가 AutoCloseable이라는 인터페이스를 구현해야만한다.
package ch8;
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();
}
}
}
class CloseableResource implements AutoCloseable{
public void exceptionWork(boolean exception) throws WorkException{
System.out.println("exceptionWork(" + exception +")가 호출됨");
if(exception)
throw new WorkException("WorkException 발생!!");
}
@Override
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); }
}
사용자 정의 예외
- 기존에 정의된 예외 클래스 외에 필요에 따라 프로그래머가 예외 클래스를 정의하여 사용할 수 있다. 보통 Exception클래스나 RuntimeException클래스로부터 상속받아 클래스를 만들지만, 필요에 따라 예외클래스를 선택할 수 있다.
- 예전에는 checkedException으로 강제하는 경우가 많았지만, 요즘은 모바일이나 웹 프로그래밍으로 자바가 주로 쓰이면서 필수적으로 처리해야하는(?) 예외들이 선택적으로 처리해도 되는 상황으로 바뀌면서 RuntimeException을 상속받아서 uncheckedException을 활용하여 주로 쓰인다고 함
예외 되던지기
- 예외가 발생할 가능성이 있는 메서드에서 try-catch문을 사용해서 예외를 처리해주고, catch문에서 필요한 작업을 행한 후에 throw문을 사용해서 예외를 다시 발생시킨다. 다시 발생한 예외는 메서드를 호출한 메서드에게 전달되고 호출한 메서드의 try-catch문에서 예외를 다시 처리해준다.
- 이 방법은 하나의 예외에 대해서 예외가 발생한 메서드와 이를 호출한 메서드 양쪽에서 처리해주어야 할 작업이 있을때 사용된다.
package ch8;
public class ExceptionEx17 {
public static void main(String[] args) {
try{
method1();
}catch (Exception e){
System.out.println("main 메서드에서 예외가 처리되었습니다.");
}
}
static void method1() throws Exception{
try{
throw new Exception();
}catch (Exception e){
System.out.println("method1메서드에서 예외가 처리되었습니다.");
throw e;
}
}
}
'Language > Java' 카테고리의 다른 글
날짜와 시간 & 형식화 date, time and formatting (0) | 2021.12.25 |
---|---|
java.lang패키지와 유용한 클래스 (0) | 2021.12.19 |
객체지향 프로그래밍2 (0) | 2021.12.05 |
완독 스터디 - 객체지향 프로그래밍1 (0) | 2021.11.28 |
직렬화와 역직렬화 (0) | 2021.11.17 |