개발/Java

[Java] try~catch

suniverse 2023. 3. 1. 17:09

🔍 오류와 예외의 차이

 

✍ 오류 (Error) 

- 프로그램 자체 또는 JVM 등의 치명적인 원인으로 발생하는 문제점이다. 

- 오류가 발생하면 프로그램은 강제로 종료된다 

- 개발자가 수정이 불가능한 문제! 

 

✍ 예외 (Exception) 

- 사용자의 잘못된 조작 실수 또는 개발자의 잘못된 코딩등으로 인해 발생하는 문제점이다. 

- 예외가 발생하면 프로그램은 강제로 종료된다. 

- 오류와 달리 개발자는 예외 상황이 발생했을 때의 대처 방안을 기술할 수 있다. 

 

✍ 예외 관련 용어 

1. 예외가 발생했을 때 예외 발생 여부를 감지하는 대상 : 리스너(Listener) 
2. 예외가 발생했을 때 정상적으로 종료될 수 있도록 처리하는 것 : 예외처리 
=> 예외가 발생하지 않도록 미리 원인을 제거하는 것은 예외 처리가 아니다. 
     예외가 발생할 상황을 미리 탐지하여 예외가 발생했을 때 
     프로그램이 정상 종료되도록 처리하는 것을 예외 처리라고 한다. 

 

✍ 예외 발생 예시 

public static void main(String[] args) {
    int a = 10;
    int b = 0;
    System.out.println("a / b = " + (a/b));
}

=> 자바에서는 나누는 수가 0일 경우 예외가 발생한다! 

 

💻

=> 예외 발생 문구 

여기서 

- Exception in thread "main" java.lang.ArithmeticException => 예외 발생 상황 정보 및 예외 발생을 탐지한 예외 클래스명 

- by Zero => 예외 발생 원인 메세지 

- at test.tet11.main(test11.java:8) => 예외 발생 위치 

 

public static void main(String[] args) {
    String str = null;
    System.out.println(str.length());
}

💻

=> 참조변수 str에 저장된 값이 없다. 그래서 str.length()에 접근하여 문자열의 길이를 구하지 못하기 때문에 예외가 발생

 

✍ 가장 대표적인 예외 발생 예시 : 배열 

public static void main(String[] args) {
    int[] arr = new int[5];
    arr[6] = 5;
    System.out.println(arr[6]);
}

💻

=> 배열 arr의 길이는 6이며 인덱스는 0~5까지이다. 그런데 배열에 존재하지 않는 인덱스 6번에 접근하려고 하니 

예외가 발생한다. 자바에서 흔히 볼 수 있는 예외상황이다. 

 


🔍 예외의 종류 

 

- 예외란 개발자가 의도하지 않은 상황에서 발생하는 문제

- 예외 발생 시 프로그램은 해당 지점에서 비정상적으로 종료된다 

- 오류와 달리 심각도가 낮으며, 예외 처리를 통해 예외 발생 시 해결책을 기술하여 

  프로그램이 정상적으로 종료되도록 처리 가능하다. 

- 예외 처리를 위해 try ~ catch 문을 사용하여 처리 작업 수행이 가능하다. 

=> try 블록 내에서 예외 발생 가능성이 있는 코드들을 관리하고 

    예외가 발생했을 경우 JVM이 보내는 예외 객체를 전달 받아 

    catch 블록 중 해당 객체 타입과 일치하는 catch 블록을 실행하여 예외를 처리한다. 

 

✍ Compile Checked Exception 계열 예외 

- 컴파일 시점에서 예외 발생 가능성을 판별하므로, 예외 처리가 되어 있지 않으면 컴파일 에러가 발생한다 
=> 즉, 예외 처리 강제성이 있다. 
- 대표적인 예 : IOException, SQLException, ClassNotFoundException

✍ Compile Unchecked Exception 계열 예외 

- 컴파일 시점에서 예외 발생 가능성을 판별할 수 없다. 
  실행 시점에서 예외 발생 여부가 판별되므로, 예외 처리 여부를 별도로 감시하지 않는다 
=> 즉, 예외 처리 강제성이 없다. 그러므로 예외 발생이 예상되는 코드를 찾아 예외 처리를 수행해야 한다. 
- 대표적인 예: RuntimeException 계열
(ArithmeticException, ArrayIndexOutOfBoundsException, NullPointerException, ClassCastException 등) 

=> java.lang 패키지 내에 Exception 클래스와 서브 클래스등이 제공되며 

각 예외는 자신의 슈퍼클래스 타입으로 업캐스팅도 가능하다. 

 

✍ try ~ catch 블록 예외 처리 예시 - ArithmeticException 예외 

public static void main(String[] args) {
    System.out.println("프로그램 시작!");
    try { // try 블록 내부에 예외 발생 가능성 코드 작성 
        System.out.println("try 블록 시작!");
        int a = 10;
        int b = 0;
        System.out.println("a / b = " + (a/b));
        System.out.println("try 블록 끝!");
    } catch (ArithmeticException e) { // 예외 처리 
        System.out.println("ArithmeticException 예외 발생!");
    }
    System.out.println("프로그램 종료!");
}

💻 예외가 발생했을 경우 

=> 나누는 수가 0일 경우 나눗셈 연산에서 예외가 발생한다. try 블록 내에서 예외 처리 후 

프로그램이 정상적으로 종료된 것을 확인할 수 있다. 

 

💻 예외가 발생하지 않았을 경우 

 

public static void main(String[] args) {
    System.out.println("프로그램 시작!");
    try { // try 블록 내부에 예외 발생 가능성 코드 작성 
        System.out.println("try 블록 시작!");
        int a = 10;
        int b = 2;
        System.out.println("a / b = " + (a/b));
        System.out.println("try 블록 끝!");
    } catch (ArithmeticException e) { // 예외 처리 
        System.out.println("ArithmeticException 예외 발생!");
    }
    System.out.println("프로그램 종료!");
}

=> try 블록 내부의 코드에서 예외가 발생하지 않았기 때문에 나눗셈 연산이 정상적으로 실행되었다. 

try 블록 종료 문자열이 출력된 후, 프로그램도 정상적으로 종료된 것을 확인할 수 있다. 

 

 

✍ try ~ catch 블록 예외 처리 예시 - ArrayIndexOutOfBoundsException

public static void main(String[] args) {
    System.out.println("프로그램 시작!");
    try {
        System.out.println("try 블록 시작!");
        int[] arr = {0, 1, 2};
        System.out.println(arr[3]);
        System.out.println("try 블록 끝!");
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println("ArrayIndexOutOfBoundsException 예외 발생!");
    }
    System.out.println("프로그램 종료!");
}

💻 예외가 발생 했을 경우 

=> 예외가 발생하여 catch문 안의 예외 발생 문자열이 출력된 후 프로그램이 종료되었다. 

 

💻 예외가 발생하지 않았을 경우 

public static void main(String[] args) {
    System.out.println("프로그램 시작!");
    try {
        System.out.println("try 블록 시작!");
        int[] arr = {0, 1, 2};
        System.out.println(arr[2]);
        System.out.println("try 블록 끝!");
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println("ArrayIndexOutOfBoundsException 예외 발생!");
    }
    System.out.println("프로그램 종료!");
}

=> try 블록 내부의 인덱스 출력문이 정상적으로 작동된 후 프로그램이 종료된 걸 확인할 수 있다. 

 

 


🔍 하나의 try 블록에서 여러개의 예외를 처리하는 경우 

- try 블록 내에서 처리해야 하는 예외가 2 종류 이상일 경우 

catch 블록을 해당 예외 종류만큼 작성하거나 

하나의 catch 블록에서 복수개의 예외를 모두 처리하는 클래스를 사용하면 된다. 

- 복수개의 catch 블록은 첫번째 catch 블록부터 차례대로 탐색한다. 

=> 만약, 끝까지 탐색했음에도 일치하는 catch 블록이 없으면 실행 시 예외가 발생한다. 

- 만약 복수개의 catch 블록 지정 시 하위 타입부터 상위 타입순으로 나열해야 한다. 

즉, ArithmeticException 보다 Exception 클래스가 위에 있을 수 없다는 말이다. 

- 만약, 하나의 catch 블록으로 복수개의 예외를 처리하려면 

  1) catch 블록의 클래스를 복수개의 예외 클래스의 상위 타입으로 지정하거나

      (모든 예외를 처리하기 위해서는 Exception  클래스를 지정)  

  2) catch 블록 클래스에 | => 버티컬바 기호를 사용하여 복수개의 클래스를 기술해도 된다. 

      (ex. FileNotFoundException | ClassNotFoundException) 

 

✍ 여러개 예외 처리 예시 

public static void main(String[] args) {
    System.out.println("프로그램 시작!");
    try {
        System.out.println("try 블록 시작!");

        // 첫번째 예외 
        int a = 10, b = 0;
        System.out.println("a / b = " + (a/b));

        // 두번째 예외 
        String str = null;
        System.out.println(str.length());

        // 세번째 예외 
        int[] arr = {0, 1, 2};
        System.out.println(arr[3]);

        System.out.println("try 블록 끝!");
    } catch (ArithmeticException e) {
        System.out.println("예외 발생! 0으로 나눌 수 없습니다.");
    } catch (NullPointerException e) {
        System.out.println("예외 발생! null값을 참조할 수 없습니다.");
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println("예외 발생! 잘못된 인덱스를 참조합니다.");
    } catch (Exception e) {
        System.out.println("예외 발생 : 나머지 예외를 모두 처리");
    }
    System.out.println("프로그램 종료!");
}

=> 만약 여기서 첫번째 예외가 발생한다면? 

 

💻

=> 예외 발생시 프로그램은 즉시 종료되므로, 두번째 세번째 코드는 실행되지 않는다.