[Java] Chap10 예외 처리

Seunghee Lee·2023년 3월 6일
0

Java

목록 보기
6/12

1. 예외와 예외 클래스

예외란 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다.

에러 vs 예외

- 공통점 : 코드나 조작이 잘못된 경우 프로그램은 곧바로 종료된다.
- 차이점 : 에러는 곧바로 종료되지만, 예외는 예외 처리를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있다.

예외는 2 가지 종류가 있다.

  1. 일반 예외(Exception)
  2. 실행 예외(Runtime Exception)
  • 일반 예외는 컴파일러 체크 예외라고도 한다.
    - 자바 소스를 컴파일하는 과정에서 예외 처리 코드가 필요한지 검사하기 때문이다.
    - 따라서 만약 예외 처리 코드가 없다면 컴파일 오류가 발생한다.

  • 실행 예외는 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외를 말한다.

🚩 일반 예외와 실행 예외는 컴파일 시 예외 처리 확인 유무만 차이일 뿐, 두 가지 모두 예외 처리가 필요하다.

  • 자바에는 예외를 클래스로 관리한다.
    - JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성한다.
    - 그리고 나서 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다.

일반 예외와 실행 예외 클래스 구별하는 방법

일반 예외는 Exception을 상속받지만 RuntimeException을 상속받지 않는 클래스들이다.
실행 예외는 RuntimeException을 상속받은 클래스들이다.


2. 실행 예외

실행 예외는 자바 컴파일러가 체크를 하지 않기 때문에 오로지 개발자의 경험에 의해서 예외 처리 코드를 삽입해야 한다.

1) NullPointerException

  • 자바 프로그램에서 가장 빈번하게 발생하는 실행 예외이다.
  • 즉, null값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생한다.
  • = 객체가 없는 상태에서 객체를 사용하려 던 것 !
public class NullPointerExceptionExample {
    public static void main(String... args) {
        String data = null;
        System.out.println(data.toString());
    }
}

2) ArrayIndexOutOfBoundsException

  • 배열에서 인덱스 범위를 초과하여 사용할 경우에 발생한다.
  • 배열크기를 지정하지 않고 사용하거나 인덱스 범위를 초과할 경우에 발생하는 예외이다.
public class ArrayIndexOutOfBoundsExceptionExample {
    public static void main(String... args) {	//인덱스 크기를 지정 안했는데
        String data1 = args[0];	//인덱스 0번을 지정하면 -> 에러 발생 !
        String data2 = args[1];

        System.out.println("args[0]: " + data1);
        System.out.println("args[1]: " + data2);
    }
}

예외 처리 방법 2가지

  1. 명령 프롬프트에서 java ArrayIndexOutOfBoundsExceptionExample 값1 값2 실행
  2. 메뉴 > Run > Run Configurations... > Arguments 탭의 Program arguments 입력란에 두 개의 매개값을 입력하고 실행하면 예외가 발생하지 않는다.

3) NumberFormatException

  • 문자열을 숫자로 변환하는 방법 중 가장 많이 사용되는 코드는 다음과 같다.
  • Integer와 Double 은 wrapper 클래스라고 한다. (자세한 건 11장에서 설명)
반환 타입메소드명(매개변수)설명
intInteger.parseInt(String s)주어진 문자열을 정수로 변환해서 리턴
doubleDouble.parseDouble(String s)주어진 문자열을 실수로 변환해서 리턴
  • 메소드를 사용해 문자열을 숫자로 변환할 수 있지만, 만약 숫자로 변환될 수 없는 문자가 포함되어 있을 경우, NumberFormatException이 발생한다.
public class NumberFormatExceptionExample {
    public static void main(String... args) {
        String data1 = "100";
        String data2 = "1a00";	//숫자 외에 다른 문자가 포함되어 있으면

        int value1= Integer.parseInt(data1);
        int value2= Integer.parseInt(data2);	//에러 발생 !

        int result = value1 + value2;
        System.out.println("value1 + value2: " + result);
    }
}

4) ClassCastException

  • 타입 변환(Casting)은 상위 클래스와 하위 클래스 간에 발생하고 구현 클래스와 인터페이스 간에도 발생한다.
  • 타입 변환에 해당하는 관계 외의 다른 클래스를 억지로 타입 변환할 경우 에러가 발생한다.

예를 들어, 실체 클래스는 Dog, Cat이고 추상 클래스는 Animal이라고 하자.
올바른 타입 변환은 다음과 같다.

Animal animal = new Dog();
Dog dog = (Dog) animal;	//같은 클래스로 타입 변환 -> O

그러나 다음과 같이 타입변환을 대입된 객체가 아닌 다른 클래스 타입으로 변환하면 ClassCastException이 발생한다.

Animal animal = new Dog();
Cat cat = (Cat) animal;	//다른 클래스로 타입 변환 -> X 
  • ClassCastException을 발생시키지 않으려면 타입 변환 전에 타입 변환이 가능한지를 확인하는 것이 좋다 ! → instanceof 연산자 사용
    - 연산자의 결과가 true 이면 → 좌항 객체를 우항 타입으로 변환이 가능하다 !
Animal animal new Dog();
if (animal instanceof Dog) {
	Dog dog = (Dog) animal;
} else if (animal instanceof Cat) {
	Cat cat = (Cat) animal;
}

3. 예외 처리 코드

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

✅ 예외 처리 코드는 try-catch-finally 블록을 이용한다.

이 블록은 생성자 내부와 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생할 경우 예외 처리를 할 수 있도록 해준다.

  • try : 예외 발생 가능 코드가 위치한다.
  • catch : try블록에서 예외가 발생하면 즉시 멈추고 catch 블록이 실행된다.
  • finally : 생략 가능하다. 예외 발생 여부와 상관없이 항상 실행할 내용이 있을 경우에만 작성한다.
public class TryCatchFinallyRuntimeExceptionExample {
    public static void main(String... args) {
        String data1 = null;
        String data2 = null;

		/* try-catch 구조 */
        try{
            data1 = args[0];
            data2 = args[1];
        } catch (ArrayIndexOutOfBoundsException e ){
            System.out.println("실행 매개값의 수가 부족합니다.");
            System.out.println("[실행 방법]");
            System.out.println("java TryCatchFinallyRuntimeExceptionExample num1 num2");
            return ;
        }

		/* try-catch-finally 구조*/
        try {
            int value1 = Integer.parseInt(data1);
            int value2 = Integer.parseInt(data2);
        } catch (NumberFormatException e) {
            System.out.println("숫자로 변환할 수 없습니다.");
        } finally {
            System.out.println("다시 실행하세요.");
        }
    }
}


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

try 블록 내부에서 발생하는 예외별로 예외 처리 코드를 다르게 처리하는 방법 ?

다중 catch를 사용한다. 여기서 catch 순서가 중요하다 !!

⚠️ 다중 catch 블록을 작성할 때 주의할 점
= 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 한다.

상위 예외 클래스에서 하위 클래스 순으로 나열하면 다음과 같다.
Exception > ClassNotFoundException InterruptException RuntimeException > ...

하나의 catch 블록에서 여러 개이 예외를 처리하는 방법 ?

멀티 catch를 사용한다. 예외 처리는 | 로 연결한다.

try { 
	... // 예외 발생 
} catch (예외1 | 예외2 예외처리변수) { 
	... 
} finally{ 
	...
} 

5. 자동 리소스 닫기

try-with-resource를 사용하여 리소스 객체의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다. (Java7 이후부터 가능)

  • 리소스 객체란 각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널 등을 말한다.

  • FileInputStream.java

public class FileInputStream implements AutoCloseable{
    private String file;

    public FileInputStream(String file) {
        this.file = file;
    }

    public void read() {
        System.out.println(file + "을 읽습니다.");
    }

    @Override	//파일 닫기 재정의
    public void close() throws Exception {	//파일 닫기 
        System.out.println(file + "을 닫습니다.");
    }
}
  • TryWithResourceExample.java
public class TryWithResourceExample {
    public static void main(String... args) {
        try (FileInputStream fis = new FileInputStream("file.txt")){
            fis.read();	//파일 읽기
            throw new Exception();
        } catch (Exception e) {
            System.out.println("예외 처리 코드가 실행되었습니다.");
        }
    }
}

6. 예외 떠넘기기

메소드 내부에서 예외가 발생할 때 try-cath-finally 블록으로 예외를 처리하는 것이 기본이지만, 경우에 따라서 메소드를 호출한 곳으로 예외를 떠넘길 수도 있다.
(별로 좋지 않음)

  • throws 키워드 사용
    : 키워드 뒤에는 떠넘길 예외 클래스를 쉼표(,)로 구분해서 나열한다.

throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야 한다.

public void method1 () {
	try {
    	method2();
    } catch (ClassNotFoundException e) {
    	//예외 처리 코드
    }
}

public void method2() throws ClassNotException {
	Class clazz = Class.forName("java.lang.String2");
}

🚩 예외를 떠넘기는 것은 별로 좋지 못한 코드이다. 프로그램 사용자는 프로그램이 알 수 없는 예외 내용을 출력하고 종료되는 것을 좋아하지 않는다. 따라서 예외가 발생한다면 try-catch-finally 블록으로 최종 처리하는 것이 바람직하다 !!

profile
자라나라 개발개발 ~..₩

0개의 댓글