자바 예외와 오류, 예외 처리

dropKick·2023년 6월 11일
0

스터디

목록 보기
14/20

목표

  • 예외와 오류를 이해한다.
  • 자바에서 예외 처리 방법을 이해한다.

예외와 오류, 예외 처리

예외와 오류의 개념과 차이

예외

  • 예외(Exception)은 프로그램의 실행 중에 예기치 않은 상황이 발생하여 정상적인 흐름을 방해하는 상황
  • 예외는 일반적으로 잘못된 입력, 파일을 찾을 수 없음, 네트워크 연결 오류 등과 같은 상황에서 발생
    • 가장 대표적인 예시는 파일 입출력에 관련된 IOException과 객체를 찾을 수 없는 NullPointerException
  • 예외는 예외 클래스를 통해 구성, Java에서는 Checked 예외와 Unchecked 예외로 구분
    • Checked 예외는 컴파일 시 체크되는 Compile Exception
    • Unchecked 예외는실행 시에 예외가 발생하는 Runtime Exception

오류

  • 오류(Error)는 프로그램이 실행 중에 심각한 시스템 수준의 문제로 인해 예기치 않은 동작이 발생하는 상황
  • 메모리 부족, 스택 오버플로우, JVM 내부 오류 등과 같은 심각한 문제로 인해 발생
  • 개발자가 처리할 수 없는 문제로 실행 중이던 프로그램이 중단되고 종료 됨

예외 처리의 중요성

  • 예외처리의 가장 큰 목적은 예외 상황을 감지하고 적절한 조치를 취함으로써 프로그램이 예기치 않은 상황에서 비정상적인 동작을 방지하는 것
  • 개발자가 인식할 수 있는 상황에서 예외 상황에 대한 정보를 기록하고 추적하고, 디버깅과 로깅을 통해 프로그램을 관리하기 위한 것
  • 또한, 사용자 예외를 통해 사용자에게 적절한 오류 메세지를 전달할 수 있음

자바 예외 클래스

  • 자바는 기본적으로 Throwable 클래스를 상속 받아 예외 클래스를 처리함
  • Exception과 Error 클래스로 분리

Checked 예외와 Unchecked 예외

Checked 예외

  • RuntimeException 클래스를 상속받지 않은 예외
  • 컴파일러에 의해 체크되어야 하는 예외로, 예외 처리를 강제화하여 반드시 예외 처리를 해주어야 함
  • 이를 위해 try-catch 문이나 throws 문을 사용하여 예외 처리할 수 있으며, IDE를 사용하는 경우 예외를 사용해야 한다는 어시스트를 제공

Unchecked 예외

  • RuntimeException 클래스를 상속받은 예외
  • 컴파일러가 체크하지 않으며, 실행 시에 예외가 발생
  • 예외 처리를 강제화하지 않으므로, 개발자의 책임 아래 예외 처리가 이루어짐
  • 대표적으로 NullPointerException, ArrayIndexOutOfBoundsException 등이 있으며 개발자가 조건을 통해 런타임 예외를 방지하도록 처리함

Exception과 Error 클래스의 관계

  • Exception 클래스는 Checked 예외와 Unchecked 예외를 모두 포함하는 기본 클래스
  • Error 클래스는 Exception 클래스의 하위 클래스로 프로그램이 복구 불가능한 상태에 이르게 하고 프로그램을 종료시키는 역할
  • 일반적으로 개발자는 Exception 클래스와 그 하위 클래스인 Checked 예외와 Unchecked 예외를 통해 예외를 처리함

예외 처리 방법

try-catch 문을 사용한 예외 처리

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ExceptionHandlingExample {
    public static void main(String[] args) {
    	// try-catch 문을 사용한 예외 처리
        try {
            int result = divide(10, 0);
            System.out.println("나눈 결과: " + result);
        } catch (ArithmeticException e) {
            System.out.println("0으로 나눌 수 없습니다.");
        }
	}
    
     // 0 나누기로 ArithmeticException 발생
    private static int divide(int num1, int num2) {
        return num1 / num2;
    }
}

여러 개의 catch 블록 사용하기

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ExceptionHandlingExample {
    public static void main(String[] args) {
    	 // 여러 개의 catch 블록 사용하기
        try {
            String str = null;
            System.out.println("문자열 길이: " + str.length());
        } catch (NullPointerException e) {
            System.out.println("Null 객체에 접근하였습니다.");
        } catch (Exception e) {
            System.out.println("예외가 발생했습니다.");
        }
	}
}

finally 블록의 역할과 활용 방법

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        // finally 블록의 역할과 활용 방법
        try {
            int result = divide(10, 2);
            System.out.println("나눈 결과: " + result);
        } catch (ArithmeticException e) {
            System.out.println("0으로 나눌 수 없습니다.");
        } finally {
            System.out.println("finally 블록 실행");
        }
	}
    
    // 0 나누기로 ArithmeticException 발생
    private static int divide(int num1, int num2) {
        return num1 / num2;
    }
}

try-with-resources 문을 사용한 자원 관리

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        // try-with-resources 문을 사용한 자원 관리
        try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
            String line = reader.readLine();
            System.out.println("파일 내용: " + line);
        } catch (IOException e) {
            System.out.println("파일을 읽을 수 없습니다.");
        }
	}
}

예외 발생과 전파

메소드에서 예외 발생시키기 (throw)

  • 개발자가 예외를 인식하고 처리하기 위해 직접 예외를 발생 시키는 경우
  • throw 키워드를 사용하여 예외 객체를 생성하고, 해당 예외를 호출한 곳으로 전달

호출자에게 예외 전파하기 (throws)

  • 실행한 메소드가 예외를 발생시키지 않고, 예외 처리를 메소드를 호출한 곳으로 위임하고 싶은 경우 throws 키워드를 사용하여 예외를 호출자에게 전파할 수 있음
  • 메소드 내에서 발생한 예외를 호출한 곳에서 처리할 수 있으며, 메소드 선언부에 throws 키워드를 사용하여 해당 메소드에서 발생할 수 있는 예외를 명시

예외 전파의 규칙

  • checked 예외는 RuntimeException 클래스를 상속받지 않은 예외들로, throws 키워드를 사용하여 예외를 선언하거나 반드시 try-catch 블록 내에서 처리
  • unchecked 예외는 RuntimeException 클래스를 상속받은 예외들로, throws 키워드로 선언하지 않아도 예외처리를 강제하지 않음

예제

public class ExceptionPropagationExample {
    public static void main(String[] args) {
    	// Unchecked Exception인 NullPointerException
        try {
            String str = null;
            int length = str.length(); // NullPointerException 발생
            System.out.println("문자열 길이: " + length);
        } catch (NullPointerException e) {
            System.out.println("NullPointerException이 발생했습니다.");
        }
        
        try {
            calculateDivision(10, 0);
        } catch (ArithmeticException e) {
            System.out.println("예외가 발생했습니다: " + e.getMessage());
        }

        try {
            readFile("invalidFile.txt");
        } catch (IOException e) {
            System.out.println("예외가 발생했습니다: " + e.getMessage());
        }
    }

    // Cheked Exception 메소드에서 예외 발생시키기 (throw)
    private static void calculateDivision(int num1, int num2) {
        if (num2 == 0) {
            throw new ArithmeticException("0으로 나눌 수 없습니다.");
        }
        int result = num1 / num2;
        System.out.println("나눈 결과: " + result);
    }

    // Cheked Exception 호출자에게 예외 전파하기 (throws)
    private static void readFile(String fileName) throws IOException {
        FileReader fileReader = new FileReader(fileName);
        BufferedReader reader = new BufferedReader(fileReader);
        String line = reader.readLine();
        System.out.println("파일 내용: " + line);
        reader.close();
    }
}

예외 처리의 디자인 패턴

일반적인 경우

public class FileProcessor {
    public void processFile(String filePath) {
        try {
            // 파일을 읽어오는 작업 수행
            FileReader fileReader = new FileReader(filePath);
            // 파일 내용 처리 작업 수행
            // ...
        } catch (FileNotFoundException e) {
            // 파일을 찾을 수 없는 경우 처리
            System.out.println("파일을 찾을 수 없습니다: " + filePath);
        } catch (IOException e) {
            // 파일 처리 중 IO 오류가 발생한 경우 처리
            System.out.println("파일 처리 중 오류가 발생했습니다: " + e.getMessage());
        } finally {
            // 사용한 리소스 정리
            closeResources();
        }
    }
    
    private void closeResources() {
        // 사용한 리소스 정리
        // ...
    }
}
  • 예외 처리의 종류를 세분화하고, 여러 개의 catch 블럭을 사용하여 가독성을 향상 시킴
  • finally 구문을 사용하여 IO 작업에 대한 리소스를 처리

복잡한 예외 처리의 경우 - Exception Chain

try {
    // 예외가 발생할 수 있는 작업 수행
} catch (Exception e) {
    // 예외를 전파하여 상위 호출자에게 처리를 맡김
    throw e;
}
  • 예외 처리를 중간에서 처리하지 않고, 호출자에게 모두 넘겨 한곳에서만 예외를 처리하도록 구성

예외 처리 메소드 추출

try {
    // 예외가 발생할 수 있는 작업 수행
} catch (Exception e) {
    handleException(e);
}

// 예외 처리 메서드
private void handleException(Exception e) {
    // 예외 처리 로직 작성
}
  • 커스텀 예외로 반복되는 예외 처리와 예외 처리 로직에 대한 별도 메소드를 추출

예외 로깅

try {
    // 예외가 발생할 수 있는 작업 수행
} catch (FileNotFoundException e) {
    logger.error("파일을 찾을 수 없습니다", e);
} catch (IOException e) {
    logger.error("IO 오류가 발생했습니다", e);
} catch (Exception e) {
    logger.error("예외가 발생했습니다", e);
}
  • 예외 발생에 대한 디버깅을 위해 로그를 통해 예외 정보를 기록

0개의 댓글