Language/Java

Java 예외처리란 : "오류가 나기 전에 방어막 치는것"

Creeper Park 2024. 1. 23. 23:38

1. 유튜브를 보고있는 사람 -> 인터넷 연결이 끊기는 <예외> 발생 -> 유튜브 못보는 <오류> 발생.&nbsp; &nbsp; 2. 달리는 차 -> 타이어가 펑크나는 <예외>발생 -> 차가 멈추는<오류>가 발생하려고 할 때 타이어를 수리하는 <예외처리>를 해주면 다시 달릴 수 있음.

 

자바는 오류를 에러와 예외로 구분한다. 에러는 코드로 수습될 수 없는 오류, 예외는 프로그래머가 미리 예측하여 코드(예외처리 코드)로 수습할 수 있는 오류를 말한다.

 

예외클래스 계층구조: 실행 시 발생할 수 있는 오류를 클래스로 정의한 것이다.

최상위 클래스로 Object클래스가 존재하고, 그 자손으로 Error(오류)와 Exception(예외) 클래스가 존재한다. Exception클래스의 자손으로는 RuntimeException과 나머지 클래스들이 있다.

모든 예외의 조상은 Exception 클래스이며, 그 자손들은 크게 두 분류로 나눌 수 있다.

RuntimeException클래스들 : 프로그래머의 실수로 발생한 예외, 컴파일러가 예외처리를 확인하지 않아 unchecked 예외라고도 한다.

Exception클래스들(런타임예외를 제외한 나머지): 사용자의 실수(입력 데이터 형식이 잘못됨) 등의 외적 요인에 의해 발생한 예외. 컴파일러가 예외처리를 확인하므로 checked 예외라고도 한다.

 

 

자바의 예외처리 구문에 대해 알아보자..

❗️try-catch 문

더보기

try {예외 발생 가능성이 있는 코드} catch (Exception1 e1) {Exception1이 발생한 경우 처리하는 코드}

catch(Exception2 e2) {Exception2가 발생한 경우 처리하는 코드} ...

하나의 try블럭 다음에는 여러 개의 catch블럭이 올 수 있다. 다만 실제로 발생한 예외의 종류와 같은 한 개의 catch블럭만 처리된다.

catch()안에는 처리하고자 하는 예외와 같은 타입(Exception1)의 참조변수(e1)를 선언해야 한다.

catch 블럭이 여러개일 경우에는 첫번째부터 차례로 내려가면서 instanceof 연산자를 이용한 검사가 진행된다. catch()안의 참조변수로 생성된 예외 클래스의 인스턴스를 참조할 수 있는지 검사하는데, 그 결과가 true인 블럭을 만날때까지 계속된다.

 

-고의적인 예외 발생 : throw 키워드를 이용한다.

public class ExceptionEx9 {
    public static void main(String args[]) {
        try {
            Exception e = new Exception("메롱");
            throw e;
        } catch (Exception e) {
            System.out.println("에러메세지 : " + e.getMessage());
		            e.printStackTrace();
//발생한 예외에 대한 정보를 불러오는 메소드.

        }
        System.out.println("정상 종료");
    }
}

실행 결과 :

에러메세지 : 메롱

정상 종료

java.lang.Exception: 메롱 at ExceptionEx9.main(ExceptionEx9.java:4)

 

 

-메소드에 예외 선언 : throws 키워드를 이용한다. 메소드의 선언부에 “throws Exception”이 있다면, 이 메소드는 모든 종류의 예외가 발생할 가능성이 있다는 뜻이다.

public class ExceptionEx12 {
    public static void main(String[]args) throws Exception {
        method1();
    }
    static void method1() throws Exception {
        method2();
    }
    static void method2() throws Exception {
        try{
        throw new Exception("ㅋㅋ나잡아봐라 메롱");}
        catch (Exception e) {
            System.out.println(e.getMessage() + "(이)라는 예외 발생..");
            System.out.println("method2: 예외를 처리했습니다.");
        }}

출력 결과 :

ㅋㅋ나잡아봐라 메롱(이)라는 예외 발생..

method2: 예외를 처리했습니다.

 

예외가 발생한 메소드 자체에서 예외를 처리할 수도 있고, 예외가 발생한 메소드를 호출한 메소드에서 처리할 수도 있다. 또는 여러 메소드가 하나의 예외처리를 분담할 수도 있다.

 

(예외를 발생시키는(던지는)메소드 : throw (예외) 가 포함됨.

예외를 처리하는 메소드 : try-catch문이 포함됨.)

 

❗️try-catch-finally 문

finally 블럭에는 예외의 발생 여부에 상관없이 무조건 실행되어야 할 코드를 적는다.
(일단 진행시켜!!!)
심지어 try블럭이나 catch 블럭에서 return문이 실행될 때에도, finally블럭의 코드들은 모두 실행된다.
(제발.. 제발 이 아이만은 살려주세요)
public class FinallyTest2 {
    public static void main(String args[]) {
        FinallyTest2.method1();
        System.out.println("메인메소드로 돌아왔습니다.");
    }

    static void method1() {
        try {
            System.out.println("method1이 호출되었습니다.");
            return;
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            System.out.println("method1- finally 블럭이 실행되었습니다.");
        }
    }
}

출력 결과 :

method1이 호출되었습니다.

method1- finally 블럭이 실행되었습니다.

메인메소드로 돌아왔습니다.

 

❗️try-with-resources 문

자바에서 입출력에 사용되는 클래스 중에서는 사용 후 꼭 닫아주어야 하는 것들이 있다고 한다. close()를 이용해서 닫아주는 방법이 있는데, close()가 예외를 발생시킬 경우 문제가 된다.

try-with-resources문은 이 점을 개선하기 위해서 생겼다. try블럭 안에 객체를 생성하는 문장을 넣으면 따로 close()를 호출하지 않아도 try블럭을 벗어나는 순간 자동으로 닫힌다. 그 다음 catch와 finally 블럭이 실행된다.

try (FileInputStream fis = new FileInputStream("score.dat");
	DataInputStream dis = new DataInputStream(fis)) {
		while(true) {
			score = dis.readInt();
			System.out.println(score);
			sum += score;
			}
} catch (EOFException e) {
	System.out.println("총점은 "+sum+" 입니다.");
} catch(IOException ie) {
	ie.printStackTrace();
}}

 

 

❗️예외 되던지기(exception re-throwing)

예외를 처리한 후에 인위적으로 다시 발생시키는 방법이다.

왜 그렇게 하냐면, 한 메소드에서 발생가능한 예외가 여러개인 경우에 몇 개는 그 메소드 내에서 자체적으로 처리하고, 선언부에 나머지를 지정하여 해당메소드를 호출한 메소드에서 처리하게 하려고이다.

더 쉽게 말하면 예외처리를 다른 메소드에게 떠넘길 수 있게 하기 위해서이다.

❗️연결된 예외(chained exception)

예외 A가 다른 예외 B를 발생시켰을 때, A를 B의 원인 예외라고 한다.

A를 원인 예외로 하는 예외 B를 발생시키려면 먼저 두 예외를 생성한 후에 B.initCause(A); 라는 문장으로 원인을 등록해준 후, B 예외를 던지면 된다.

마찬가지로 이런게 왜 있냐고 하면.. checked 예외(Exception클래스들)를 unchecked 예외(RuntimeException 클래스들)로 바꾸는 데 유용하다.

자바가 탄생했을 초기(1990년대) 에는 견고한 코드 작성을 위해 예외처리를 강제했는데(checked 예외 사용) 지금은 그 때와 컴퓨터 환경이 많이 달라져 예외처리가 꼭 필수가 아니게 되었다.

uncheck예외로 바꾸면 예외처리가 선택사항이 되므로, 의미없는 try-catch문의 사용을 줄일 수 있다.