언어/Java

예외 처리

O_oz 2023. 10. 3. 07:39
반응형

에러 (error) : 컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것

예외 (exception) : 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류

    - 예외 처리 (Exception Handling)을 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있음

    - 일반 예외 (Exception) : 컴파일러 체크 예외 / 자바 소스 파일을 컴파일하는 과정에서 예외 처리 코드가 필요한지 검사 > 예외 처리 코드가 없다면 컴파일 오류 발생

    - 실행 예외 (Runtime Exception) : 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외

    - JVM은 프로그램 실행 도중 예외가 발생하면 해당 예외 클래스로 객체를 생성하고 예외 처리 코드에서 예외 객체를 이용할 수 있도록함

    - 모든 예외 클래스들은 java.lang.Exception 클래스를 상속 받음

 

1. 실행 예외 : 자바 컴파일러가 체크하지 않기 때문에 개발자의 경험에 의해 예외 처리 코드를 삽입해야 함

NullPointer Exception : 객체 참조가 없는 null 값을 갖는 참조 변수로 객체 접근 연산자인 도트 (.)를 사용했을 때 발생

 

ArrrayIndexOutOfBoundsException : 배열에서 인덱스 범위를 초과하여 사용할 경우 발생

    - 실행 매개값이 없거나 부족할 경우 조건문을 이용해서 사용자에게 실행 방법을 알려주는 등, 배열값을 읽기 전에 배열의 길이를 먼저 조사하는 방법을 통해 예외를 처리함

 

NumberFormatException : 숫자로 변경 불가능한 문자열로 되어 있는 데이터를 숫자로 변경하는 경우 발생

반환 타입 메소드명(매개 변수) 설명
int Integer.parseInt(String s) 주어진 문자열을 정수로 변환해서 리턴
double Double.parseDouble(String s) 주어진 문자열을 실수로 변환해서 리턴

    - 매개값인 문자열이 숫자로 변환될 수 있다면 숫자를 리턴하지만, 숫자로 변환될 수 없는 문자가 포함되어 있다면 java.lang.NumberFormatException을 발생시킴

 

ClassCastException : 억지로 타입 변환을 시도할 경우 발생

    - 상속 관계나 인터페이스와 구현 클래스간의 관계가 아닐 경우 다른 클래스로 타입 변환할 수 없음

    - 타입 변환 전에 타입 변환이 가능한지 instanceof 연산자로 확인하여 예외를 처리함

 

2. 예외 처리 코드 : 프로그램에서 예외가 발생했을 경우 프로그램의 갑작스러운 종료를 막고, 정상 실행을 유지할 수 있도록 처리하는 코드

try-catch-finally 블록 : 생성자 내부와 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생할 경우 예외 처리를 가능하게 하는 구문

정상 실행 경우 예외 발생 경우
try {                                                // ①
                                         
            예외 발생 가능 코드
                                    
} catch(예외클래스 e) {
                          
            예외 처리
               
} finally {                                        // ②

            항상 실행;

}
try {                                                // ①
                                         
            예외 발생 가능 코드              // 예외 발생
                                    
} catch(예외클래스 e) {                  // ②
                          
            예외 처리
               
} finally {                                        // ③

            항상 실행;

}

    - try 블록 : 예외 발생 코드가 위치 / 예외 발생 없이 정상 실행되면 catch 블록은 실행되지 않고 finally 블록을 실행하며 예외가 발생하면 즉시 실행을 멈추고 catch 블록 실행 후 finally 블록을 실행함

    - finally 블록 : 예외 발생 여부와 상관없이 항상 실행할 내용이 있을 경우에만 작성 / try 블록과 catch 블록에서 return문을 사용하더라도 finally 블록은 항상 실행됨

 

3. 예외 종류에 따른 처리 코드

다중 catch

    - 발생되는 예외별로 예외 처리 코드를 다르게 하여 예외 처리 가능

    - catch 블록이 여러 개지만 단 하나의 catch 블록만 실행됨

    - 다중 catch 블록 작성 시 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 함

 

멀티 catch

    - 하나의 catch 블록에서 여러 개의 예외를 처리 가능하게 함

    - catch 괄호 () 안에 동일하게 처리하고 싶은 예외를 |로 연결

public class Example {
    public static void main(String[] args) {
        try {
            String data1 = args[0];
            String data2 = args[1];
            int value1 = Integer.parseInt(data1);
            int value2 = Integer.parseInt(data2);
            int result = value1 + value2;
            System.out.println(data1 + " + " + data2 + " + " + result);
        } catch(ArrayIndexOutOfBoundsException | NumberFormatException e) { // 멀티 catch
            System.out.println("실행 매개값의 수가 부족하거나 숫자로 변환할 수 없습니다..");
        } catch(Exception e) {		// 다중 catch
            System.out.println("알수 없는 예외 발생");
        } finally {
            System.out.println("다시 실행하세요.");
        }
    }
}

 

4. 자동 리소스 닫기

try-with-resources : 예외 발생 여부와 상관없이 사용했던 리소스 객체의 close() 메소드를 호출하여 안전하게 리소스를 닫음

리소스 : 각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널 등의 데이터를 읽고 쓰는 객체

FileInputStream = null;
try {
    fis = new FileInputStream("File.txt");
    ···
} catch(IOException e) {
    ···
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch(IOException e) {}
    }
}

위와 같은 코드를 아래와 같이 줄일 수 있음

try(FileInputStream fis = new FileInputStream("File.txt")) {
    ···
} catch(IOException e) {
    ···
}

    - close()를 명시적으로 호출한 곳이 없음

    - try 블록이 정상적으로 실행됐거나 도중에 예외가 발생하게되면 자동으로 FileInputStream의 close() 메소드가 호출됨

    - try 블록에서 예외가 발생하면 우선 close()로 리소스를 닫고 catch 블록을 실행

 

사용 조건 : 리소스 객체가 close() 메소드가 정의되어 있는 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 함

 

5. 예외 떠넘기기

경우에 따라 메소드를 호출한 곳으로 예외를 떠넘기는 방법

throws 키워드 : 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 함 / 떠넘길 예외 클래스를 수미표로 구분해서 나열

    - throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야하며 catch 블록에서 떠넘겨 받은 예외를 처리해야 함

    - main() 메소드에서도 throws 키워드로 예외를 떠넘길 수 있는데, 이는 JVM이 최종적으로 예외 처리를 하게 됨

리턴타입 메소드명(매개변수, ···) throws Exception {
}

위와 같은 형식으로 모든 예외를 간단히 떠넘길 수 있음

public class Example {
    public static void main(String[] args) {
        try {
            findClass();
        } catch(ClassNotFoundException e) {
            System.out.println("클래스가 존재하지 않습니다.");
        }
    }
    
    public static void findClass() throws ClassNotFoundException {
        Class clazz = Class.forname("java.lang.String2");
    }
}

    - forName() 메소드는 선언부 뒤에 throws ClassNotFoundException이 붙어 있기 때문에 forname() 메소드를 호출할 때 try-catch 블록으로 예외를 처리하거나, throws로 예외를 떠넘겨야 함

 

6. 사용자 정의 예외

자바 표준 API에서 제공하는 예외 클래스 이외에 개발자가 직접 정의해서 만들어야 하는 예외 클래스

 

예외 클래스 선언

    - 일반 예외(컴파일러가 체크)로 선언시 Exception 상속, 실행 예외(컴파일러가 체크 X)로 선언시 RuntimeException 상속

    - 클래스 이름이 Exception으로 끝나는 것이 좋음

    - 필드, 생성자, 메소드 선언들을 포함할 수 있지만, 대부분 생성자 선언만 포함

    - 생성자는 두 개 선언이 일반적 : 매개 변수 없는 일반 기본 생성자 + 예외 발생 원인을 전달하기 위한 String 타입의 매개 변수

    - String 타입의 매개 변수를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨줌

public class BalanceInsufficientException extends Exception {
    public BalanceInsufficientException() {}
    public BalanceInsufficientException(String message) {
        super(message);
    }
}

 

예외 발생 시키기

throw new XXXException();
throw new XXXException("메시지");

    - 에외 발생 코드를 가지고 있는 메소드는 내부에서 try-catch 블록으로 예외 처리 가능

    - 대부분 자신을 호출한 곳에서 예외를 처리하도록 throws 키워드로 예외를 떠넘김

public class Account {
    private long balance;
    
    public Account() {}
    
    public long getBalance() {
        return balance;
    }
    
    public void deposit(int money) {
        balance += money;
    }
    
    public void withdraw(int money) throws BalanceInsufficientException {
        if (balance < money) {
            throw new BalanceInsufficientException("잔고부족 : " + (money-balance) + " 모자람");
        }
        balance -= money;
    }
}

 

7. 예외 정보 얻기

try 블록에서 예외가 발생하면 예외 객체는 catch 블록의 매개 변수에서 참조하게 되므로 매개 변수를 이용하면 예외 객체의 정보를 알 수 있음

    - 모든 예외 객체는 Exception 클래스를 상속하기 때문에 Exception이 가진 모든 메소드들은 모든 예외 객체에서 호출 가능

    - 예외 발생 시 String 타입의 메시지를 갖는 생성자를 이용했다면, 메시지는 자동적으로 예외 객체 내부에 저장됨

    - 예외 메시지의 내용에는 왜 예외가 발생했는지에 대한 간단한 설명이 포함되어야 함 > 예외 코드로 세분화 할 수도 있음

    - 주로 getMessage()와 printStackTrace() 메소드를 많이 사용함

try {
    ···
} catch(XXXException e) {
    // 예외가 가지고 있는 Message 얻기
    String message = e.getMessage();
    
    // 예외의 발생 경로를 추적
    e.printStackTrace();
}

 

반응형

'언어 > Java' 카테고리의 다른 글

중첩 클래스와 인터페이스  (0) 2023.08.29
인터페이스  (1) 2023.08.27
클래스의 타입 변환과 다형성  (0) 2023.08.27
메소드 재정의 (오버라이딩)  (0) 2023.08.24
상속  (0) 2023.08.24