본문 바로가기

개발/Java

[Java] 자바 - 에러, 예외(Exception) / 예외처리구문 ( try ~ catch문 ) / throws (수정)

 

< 에러 >

 

[ 종류 ]

시스템 에러 컴퓨터의 오작동으로 인해 발생하는 에러
컴파일 에러 프로그램 실행 전, 실행을 막는 소스코드상의 문법적인 문제로 발생하는 에러
런타임 에러 프로그램 실행 중 발생하는 에러
논리 에러 소스코드상 문법적인 문제가 없고 실행시 오류가 발생하지 않지만, 프로그램의 의도와 맞지 않는 실행결과가 나오는 에러

 

< 예외 (Exception) >

 

- 시스템에러를 제외한 나머지 컴파일, 런타임 논리에러와 같이 비교적 덜 심각한 에러

- 소스코드 수정으로 해결 가능한 에러. 특히, 런타임에러를 주로 예외로 다룸

uncheckedException  - 꼭 예외처리를 할 필요는 없는 예외
 - 실행해야 예외가 발생하는지 알 수 있음
checkedException  - 문법적으로 반드시 예외처리를 해야하는 예외
 - 실행 전, 예외처리를 하지 않았음을 알려줌
    ex) 오류 문구 - Unhandled exception type IOException

 

[ RuntimeException ] 세부 예외들을 자식클래스로 가지고 있음

- NullPointerException : 주소값 객체를 참조했을때 null이 들어있을 경우 발생하는 예외

- ArrayIndexOutOfBoundsException : 배열의 부적절한 인덱스로 접근할 때 발생하는 예외

- ClassCastException : 허용할 수 없는 형변환을 진행할 경우 발생하는 예외

- ArithmeticException : 나누기 연산시, 0으로 나누면 발생하는 예외

- NegativeArraySizeException : 배열 크기를 음수로 지정할 경우 발생하는 예외

- InputMismatchException : 사용자에게 입력 값을 받을때, 다른 자료형을 입력하는 경우 발생하는 예외

.... 그외 다수

 

 

< 예외처리 >

 

- 예외들이 발생했을 경우에 대비해서 처리하는 방법을 정의해두는 것

 

[ 방법 ]

1. try ~ catch문을 이용

- 문제 발생 시, 해당 구문을 출력 후 코드를 이어가게 함. 문제를 해결하는 것은 아님

- 문제가 발생한 구문에서 바로 catch문으로 이동함. 문제발생 이후 구문은 무시

- 다중 catch문 가능 : catch할 예외가 여러개일 경우 사용

- 에러들이 RuntimeException의 자식 클래스들이므로,

  catch문에 RuntimeException을 사용하여 모든 오류 받기 가능(다형성 이용)

[ 표현법 ]

try {

        예외가 발생할법한 구문

} catch (발생할 예외 클래스이름  변수명) { → 변수명 'e' 사용

        해당 예외가 발생했을 때 실행할 구문

} finally {

        예외가 발생하더라도 반드시 실행할 구문

}

public void method1() {
    // 사용자에게 두 개의 정수값을 입력받아서 나눗셈 결과 출력
    Scanner sc = new Scanner(System.in);

    System.out.print("첫번째 정수 > ");
    int num1 = sc.nextInt();
    System.out.print("두번째 정수 > ");
    int num2 = sc.nextInt();

    // 예외처리 구문
    try {
        System.out.println("나눗셈 결과 : " + (num1 / num2));
        // 문제가 난 라인에서 catch문으로 이동함. ↓아래 구문은 출력하지 않음. 무시하고 catch로
        System.out.println("문제 발생 없음"); 
    } catch (ArithmeticException e) { // 매개변수명으로 주로 e 사용
        System.out.println("0 안돼. 돌아가."); // 해당 오류 발생시 출력할 구문
        e.printStackTrace(); // 오류를 추적할 수 있는 메소드(현재 발생한 오류의 정보를 출력)
    }
    System.out.println("종료합니다.");
}

- printStackTrace() : 오류를 추적할 수 있는 메소드

                                 현재 오류가 발생한 정보를 볼 수 있고, 어디서 에러가 났는지 나열

                                 개발, 테스트 단계에서만 사용. 사용자에게 보여지지않도록 주의 (취약점 노출)

 

[ 다중 catch문 ]

public void method3() {
    // 사용자로부터 배열의 크기를 입력받아서 배열 만들기, 100번째 인덱스값 출력
    Scanner sc = new Scanner(System.in);
    System.out.print("배열의 크기 : ");

    try { // 3가지 예외 발생 가능
        int size = sc.nextInt(); // 1. InputMismatchException
        int[] arr = new int[size]; // 2. NegativeArraySizeException
        System.out.println(arr[100]); // 3. ArrayIndexOutOfBoundsException
    } catch(InputMismatchException e) {
        System.out.println("정수를 입력해주세요.");
    } catch(NegativeArraySizeException e) {
        System.out.println("양수를 입력해주세요.");
    } catch(ArrayIndexOutOfBoundsException e) {
        System.out.println("100칸 배열이 없습니다.");
    } catch(RuntimeException e) { // 부모클래스인 RuntimeException으로 자식클래스 예외 모두 받음
    	System.out.println("배열 오류 발생");
    }
    // 다중 catch문 작성시, 범위가 작은 자식타입의 예외클래스부터 먼저 기술해야 함
    // (RuntimeException을 먼저 적으면 아래 catch문으로 갈 수 없음)
    
    System.out.println("프로그램을 종료합니다.");
}

[ try ~ catch ~ finally ]

public void fileSave() {
    // FileWriter : 파일로 데이터를 2Byte 단위로 출력하는 스트림

    FileWriter fw = null; // try구문과 분리		
    try {
        // 1. 객체 생성 => 파일과 연결된 통로 생성
        fw = new FileWriter("b_char.txt"); // 예외처리 : IOException

        // 2. 파일에 쓰기 => write()
        fw.write("출력하는 중\n"); 
        fw.write("B");
        char[] arr = {'a', 'b', 'c', 'd'};
        fw.write(arr); // 오버로딩 적용
    } catch(IOException e) {
        e.printStackTrace();
    } finally { // try에서 오류가 발생하더라도 반드시 실행할 구문 작성
        // 3. 자원반납 => close() 통로 닫기
        try {
            fw.close(); // 예외처리 : IOException
            // 자원반납을 해서 통로를 닫지 않으면 데이터가 입력되지 않음
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}

 

2. throws를 이용 (떠넘기기)

- 문제가 발생한 메소드가 아닌, 메소드를 호출한 곳에 예외를 떠넘겨서(위임해서) 처리

[ 사용 이유 ] 오류를 기록하는 로그에서 해당 오류를 넘겨받을 수 있게 하기 위해 사용

public void method2() throws IOException { // 클래스 옆에 throws 작성
    System.out.println("아무 문자열이나 입력해주세요 : ");
    
    // Scanner와 같이 키보드로 값을 입력받을 수 있는 객체(문자열로만 가능)
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    String str = br.readLine(); // 여기서 발생하는 예외처리를 메소드를 호출한 곳에서 해결하게함
                                // 예외처리 하지 않으면 오류 출력(Unhandled exception type IOException)
    System.out.println("문자열 길이 : " + str.length());	
}

public void method1() {
    try {
        method2(); // '문제가 발생한 메소드를 호출한 곳'에서 예외처리 해야함
    } catch(IOException e) {
        System.out.println("예외 발생");
    }
}