Java_문법뽀개기 04

5w31892p·2022년 11월 15일
0

Java

목록 보기
5/17

📜 Java

:: ✍ 예외, 에러처리

에러를 처리하는 방법

:: 예외처리 (Exception Handling, Error Handling)

  • 다양한 예외 상황에 대응하기 위한 예외 처리 코드 필요
    • 코드 잘못 짠 경우
    • 대표적으로 인터넷 연결이 끊어진 경우
  • 예외처리의 목적
    1. 예외 발생으로 인해 실행 중인 프로그램의 비정상 종료를 막기 위해
    2. 개발자에게 알려서 코드 보완할 수 있도록 하기 위해
  • 상속을 이용해 모든 예외 표현
    • 모든 예외 클래스는 Throwable의 자식 클래스

Throwable

두 종류의 자식 클래스가 있음

  1. Error
    • 프로그램 종료되어야 하는 심각한 문제 표현
    • 대부분 컴퓨터나 JVM이 시스템적으로 동작할 수 없는 상황
    • 대표적인 에러 OOM
Java는 JVM 내 Heap이라는 메모리 공간을 운영체제로 할당 받아 사용함
할당 받을 수 있는 최대 메모리 이상 사용하게 되면 JVM 다운
이 경우 OutOfMemoryError가 나면서 프로그램 종료됨

Java의 대표적인 에러 상황 : OOM
  1. Exception

    • 프로그램이 종료되지는 않지만 예외나 문제상황 표현하기 위해 사용



새로운 예외 정의하고 싶다면
Throwable 또는 그 하위에 있는 예외 클래스를 상속받아 정의
  • 현재 표현하려는 예외 상황 대부분은 Exception 종류
  • 실행 도중 발생하는 에러
    • RuntimeException 상속받아 정의
      • 코드문제는 RuntimeException 상속받는 것을 추천
  • 파일을 읽고 쓰거나, 네트워크의 데이터를 읽고 쓸 때 발생하는 에러
    • IOException 상속받아 정의

:: try-catch(-finally) 형식

try

  • try라는 구문으로 원래 실행하려고 했던 코드를 try 블록 안에 구현

catch

  • 알고 있는 예외 발생 상황 and 그것을 다뤄야할 사항 구현
  • 괄호 안에 어떤 Exception에 해당하는지 그리고 그 변수의 이름은 뭔지 선언하고 블록 안에 다루기 위한 코드를 작성

catch문은 여러가지 예외에 대해 중복 catch 블럭을 사용하여 이어서 쓸 수 있는데
앞에 있는 catch문에 잡혀서 그 블록을 실행했다면 그 뒤에 있는 catch문에는 잡히지 않음 (catch문들로는 전파 되지 않음)
즉, 에러가 앞에서 잡혔으면 뒤에선 잡히지 않는다는 것

∴ 좁은 범위의 예외부터 앞에 선언하는 것이 좋음

  • 좁은 범위란 상속관계에서 자식 클래스에 위치 할수록 좁은 범위
예를 들어서 IOException이 발생할 것 같아 예외처리를 하고, 
그 외의 예외도 예외처리를 하고 싶다면 
IOException을 catch 하는 구문을 먼저 Exception 을 catch하는 구문을 그 뒤에 작성

finally

  • 꼭 필요하진 않음
  • try-catch 만 있어도 동작함
  • 블록 안에 예외의 발생여부에 관계없이 항상 수행되어야하는 코드 작성
  • 즉, 성공했든 실패했든 try-catch 구문 모두 실행되고 마지막에 항상 실행되어야 하는 코드를 작성
  • 만약, 예외가 발생하지 않는다면 try → finally 순으로 실행

모든 Exceptiondms getMessage를 가짐

  • getMessage에는 Exception 예외의 이유 설명하는 글자가 담겨 있음
public class Main {
    public static void main(String[] args) {
        int number = 10;
        int result;

        for (int i = 10; i >= 0; i--) {
            try {
                result = number / i;
                System.out.println(result);
            } catch (Exception e) {
                System.out.println("Exception발생: " + e.getMessage()); // getMessage
            } finally {
                System.out.println("항상 실행되는 finally 구문");
            }
        }
    }
}

0으로 나눠지는 경우 catch문이 실행

1
항상 실행되는 finally 구문
1
항상 실행되는 finally 구문
1
항상 실행되는 finally 구문
1
항상 실행되는 finally 구문
1
항상 실행되는 finally 구문
2
항상 실행되는 finally 구문
2
항상 실행되는 finally 구문
3
항상 실행되는 finally 구문
5
항상 실행되는 finally 구문
10
항상 실행되는 finally 구문
Exception발생: / by zero
항상 실행되는 finally 구문

:: try-with-resource 형식

  • 입출력과 함께 자주 쓰이는 구문
  • try-with-resource문은 try문을 벗어나는 순간 close() 자동호출
  • 기존의 try-catch(-finally)문은 자원을 닫을 때 close()를 사용
  • try()안의 입출력 스트림을 생성하는 로직을 작성할 때 해당 객체가 AutoClosable 인터페이스를 구현한 객체 사용
    • AutoClosable 인터페이스에는 예외가 발생할 경우 close()메소드를 호출하기로 정의되어있기 때문에

catch문 작성 전

  • FileOutputStream, write, flush에 빨간줄
    • 다루지 않은 예외가 FileOutputStream을 만들 때 FileNotFoundException이라는게 만들어 질 수 있고
    • write, flush 할 때에는 IOException이 발생할 수 있다고 미리 경고하는 것

catch문 작성 후

  • 빨간 줄 사라짐
    • FileOutputStream은 크게 보면 IOException에 해당하기 때문에 catch문에서 다 걸림

try 괄호 안에는 AutoClosable 인터페이스 구현한 클래스만 올 수 있음

  • AutoClosable 인터페이스에는 예외가 발생할 경우 close()메소드를 호출하기로 정의되어있기 때문에
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {

        try (FileOutputStream out = new FileOutputStream("test.txt")) { // test.txt file 에 Hello Sparta 를 출력
            out.write("Hello Sparta".getBytes());
            out.flush();

            // catch문 작성 이전엔 FileOutputStream, write, flush에 빨간줄
            // 다루지 않은 예외가 FileOutputStream을 만들 때 FileNotFoundException이라는게 만들어 질 수 있고
            // write, flush 할 때에는 IOException이 발생할 수 있다고 미리 경고하는 것
            // catch문 쓰니까 사라짐
            // FileOutputStream은 크게 보면 IOException에 해당하기 때문에 catch문에서 다 걸림

        } catch (IOException e) {
            System.out.println("IOException 발생 : "+ e.getMessage());
            e.printStackTrace();
        }
    }
}
// test 파일 생성되고 , 그 안에 Hello Sparta 적혀있음
  • 에러가 발생하지 않더라도 에러 핸들링에 대한 코드가 필요할 수 있음
  • try 뒤에 괄호가 있어서 그 안에 새로운 변수 할당
    • 괄호안에 올 수 있는 것은 AutoClosable 인터페이스 구현한 클래스만 올 수 있음

위 코드를 try catch문을 사용했다면

  • 코드 길어지고
  • FileOutputStream 을 열고 닫을때 생기는 Exception 까지 그 상위에서 catch를 하거나 throws로 감싸줘야함
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        FileOutputStream out = new FileOutputStream("test.txt");
        try {
            // test.txt file 에 Hello Sparta 를 출력
            out.write("Hello Sparta".getBytes());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        out.close();
    }
}

:: 메소드에서 예외 선언

  • catch문을 이용해서 예외처리를 하지 않은 경우
    • 메소드에 throws로 예외가 발생할 수 있다는 것을 알려주어야 함
  • throws 키워드가 있는 함수를 호출한다면, caller 쪽에서 catch와 관련된 코드를 작성
  • 즉, throws 를 사용해서 메소드 내에서 발생할 수 있는 예외를 적어주면 됨
void method() throws IndexOutOfBoundsException, IllegalArgumentException {
    //메소드의 내용
}

:: ✍ 예외, 에러 처리 퀴즈

divide() 함수는 매개변수(parameter)에 들어오는 값에 따라서
ArithmeticException과 ArrayIndexOutOfBoundsException이 발생

1. throws 키워드를 통해서 divide() 함수에서 발생할 수 있는 exception의 종류가 무엇인지 알게 해주세요.
2. Main 함수에서 try-catch 문을 이용해서, 다음 동작을 구현하세요.
   a. ArithmeticException이 발생할 때는 잘못된 계산임을 알리는 문구 출력
   b. ArrayIndexOutOfBoundsException이 발생할 때는 현재 배열의 index범위를 알려주는 문구 출력
  1. throws로 발생할 수 있는 에러 명세
class ArrayCalculation {

    int[] arr = {0, 1, 2, 3, 4};

    public int divide(int denominatorIndex, int numeratorIndex)
            throws ArithmeticException, ArrayIndexOutOfBoundsException {
            // 이런 에러들이 발생할 수 있다고 명세
            // 명세를 하면 이 함수를 사용하는 쪽에선 이런 에러에서 핸들링해야겠다고 생각할 수 있음
        return arr[denominatorIndex] / arr[numeratorIndex];
    }
}

public class Main {
    public static void main(String[] args) {
        ArrayCalculation arrayCalculation = new ArrayCalculation();

        System.out.println("2 / 1 = " + arrayCalculation.divide(2, 1));
        try {
            System.out.println(
                    "1 / 0 = " + arrayCalculation.divide(1, 0));
        } catch (ArithmeticException arithmeticException) {
            System.out.println("잘못된 계산입니다. " + arithmeticException.getMessage());
        }
        try {
            System.out.println("Try to divide using out of index element = "
                    + arrayCalculation.divide(5, 0));
        } catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
            System.out.println(
                    "잘못된 index 범위로 나누었습니다. 타당 index 범위는 0부터" + (arrayCalculation.arr.length - 1) + "까지 입니다.");
        }
    }
}
2 / 1 = 2
잘못된 계산입니다. / by zero
잘못된 index 범위로 나누었습니다. 타당 index 범위는 0부터4까지 입니다.

by zero : 0으로 나눴다고 알려주는 것

문제 발생시 Exception 핸들링 코드 작성해서 더 견고한 코드 만들 수 있음

0개의 댓글