Java/STUDY HALLE

[Java] 예외처리

무토(MUTO) 2021. 1. 15. 22:07

0. 학습 목표

  • 자바에서 예외 처리 방법(try, catch, throw, throws, finally)
  • 자바가 제공하는 예외 계층 구조
  • Exception과 Error의 차이
  • RuntimeException과 RuntimeException이 아닌것의 차이는?
  • 커스텀한 예외 만드는 방법

1. 자바에서의 예외처리방법

  • Java에서 Exception이란 프로그램의 실행 도중에 발생하는 문제상황 을 의미한다.
  • if문을 사용하여 예외 처리를 진행할 수도 있지만 if문은 프로그램의 로직을 작성하는 부분도 존재하기 때문에 if문으로 예외처리를 진행할 시, 예외사항이 발생하는곳을 찾아보기 어렵다는 단점이 있다. 따라서 예외처리 구문을 통해 예외를 처리하는것이 바람직하다.

1-1. try - catch - finally

Main.java

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr;
        try {
            arr = new int[n];
        }catch (NegativeArraySizeException e) {
            System.out.println("Exception occur");
        } finally {
            System.out.println("finally");
        }
        System.out.println("Hello Java Exception!!!");
    }
}

1-1-1. try

  • 실행하고싶은 로직을 작성하는 공간이다. 내가 작성하려고 했던 코드를 try 이후의 블록에 넣으면 된다.

1-1-2. catch(Exception e)

  • try 구문에서 Exception이 발생했을때 실행되는 부분이다.
  • 발생할 예외클래스의 타입을 매개변수로 주입한다. 그 후 블록에 예외가 발생했을 때 필요한 로직들을 작성한다.

catch 구문은 여러번 중복하여 작성할 수도 있다.

package study.moon.Atest;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr;
        try {
            arr = new int[n];
        }catch (NegativeArraySizeException e) {
            System.out.println("Exception occur1");
        }catch (IndexOutOfBoundsException e) {
            System.out.println("Exception occur2");
        }catch (NullPointerException e) {
            System.out.println("Exception occur3");
        } finally {
            System.out.println("finally");
        }
        System.out.println("Hello Java Exception!!!");


    }
}

 

catch 구문에서 사용할 코드들이 같다면 '|' 를 사용하면 하나의 블록에서도 표현이 가능하다.

package study.moon.Atest;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr;
        try {
            arr = new int[n];
        }catch (NegativeArraySizeException | IndexOutOfBoundsException | NullPointerException e) {
            System.out.println("Exception occur1");
        } finally {
            System.out.println("finally");
        }
        System.out.println("Hello Java Exception!!!");

    }
}

1-1-3. finally

  • try에 진입했다면 무조건 실행되는 부분이다.

굳이 필요없는기능같은데...??? 생략해도 되지 않을까요?

return을 읽어도 finally는 동작한다.

package study.moon.Atest;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr;
        try {
            arr = new int[n];
            return;          //이렇게 return을 하더라도
        }catch (NegativeArraySizeException | IndexOutOfBoundsException | NullPointerException e) {
            System.out.println("Exception occur1");
        } finally {
            System.out.println("finally");//finally는 읽어야지
        }
        System.out.println("Hello Java Exception!!!");

    }
}
  • 뿐만 아니라 이러한 finally는 DB 혹은 File 처리를 진행할 때 예외가 발생한 상황에 close()를 반드시 호출해주어야하는데 그런 작업을 할 때에도 유용하게 사용된다.

  • 또한 finally는 사실 생략해도 된다.

finally 생략

package study.moon.Atest;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr;
        try {
            arr = new int[n];
            return;
        }catch (NegativeArraySizeException | IndexOutOfBoundsException | NullPointerException e) {
            System.out.println("Exception occur1");
        }
        System.out.println("There is no finally!!!");

    }
}

1-1-4. printStackTrace()

  • 예외 발생 당시의 호출 스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력한다.

1-1-5. getMassage()

  • 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

1-2. throw, throws

1-2-1. throw

내가 임의로 예외를 발생시킬 수 있다.

임의의 예외발생

package study.moon.Atest;

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        if (n == 1) {
            throw new IndexOutOfBoundsException();
        } else if (n==2) {
            throw new NegativeArraySizeException();
        } else if (n==3) {
            throw new IllegalArgumentException();
        }
        System.out.println("Hello Throw");
    }
}

1-2-2. throws

해당 메서드를 호출한 상위 메서드로 예외를 위임한다.

throws 뒤에 해당 메서드를 호출한 메서드로 위임할 예외를 작성한다.

package study.moon.Atest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
    public static void main(String[] args) throws IOException {//readLine()메서드가 throws IOException을 포함하고 있기 때문에 메인메서드에서 jvm 으로 예외처리를 위임한다.
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int n = Integer.parseInt(br.readLine());//readLine()메서드가 throws IOException을 포함하고 있다.
        bw.write(n+"");
        br.close();
        bw.flush();
        bw.close();
    }
}

try-with-resources

만약 finally를 close를 호출해야 하기 때문에 사용한다면 이 문법을 사용하는것이 좋다.
해달 클래스가 AutoClosable이라는 인터페이스를 구현했다면 사용가능하다.

기존의 문법

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String str;
        try{
            str = br.readLine();
        } catch (IOException e) {
            e.getStackTrace();
        }finally {
            br.close();
            bw.close();
        }
    }
}

새로 추가된 문법

package study.moon.Atest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            String str;
            str = br.readLine();
        } catch (IOException e) {
            e.getStackTrace();
        }
    }
}

2. 자바가 제공하는 예외 계층 구조

https://madplay.github.io/post/java-checked-unchecked-exceptions

3. Error VS Exception

3-1. Error

개발자가 미리 예측하여 처리할 수 없는 심각한 오류. 프로세스가 종료됨.

3-2. Exception

개발자가 예측하여 문제가 발생하지 않도록 예외처리를 해주어야 하는 오류.

ClassNotFoundException VS NoClassDefFoundError

비슷해 보이지만 문제를 해결하려면 하나는 골때리고 하나는 해결하기 쉬운 문제이다.
전자는 이전에 공부한 것처럼 클래스로더가 클래스패스에서 해당 클래스를 찾지 못하면 발생하는 예외이다. 간단하게 클래스패스에 해당 클래스가 있는지 확인해주면 된다.
후자는 컴파일타임때 클래스패스에 클래스가 존재하지만 런타임때 클래스가 변경되거나 클래스의 스태틱 초기화가 예외를 던질 경우에 해당 에러가 발생한다. 따라서 해당 클래스를 의존하는 모든 관계를 살펴봐야한다.

NoClassDefFoundError 예제


- 해당 소스를 컴파일하고 Test.class를 삭제하고 실행시키면 다음과 같은 상황이 발생한다.

4. RuntimeException VS !RuntimeException

말그대로 런타임에 발생하는 예외와 런타임에 발생하지 않고 컴파일타임에 발생하는 예외의 차이가 있다.
따라서 런타임 예외는 따로 예외처리를 해주지 않아도 된다.
반면에 비 런타임 예외는 예상할 수 있는 예외이기 때문에 예외처리를 반드시 해주어야 한다.

5. 커스텀한 예외 만드는 방법

자바에서는 Exception 클래스를 상속받아 내가 원하는 예외처리를 추가적으로 만들어낼 수 있다.
"Hello World"를 입력하면 예외를 발생시키는 프로그램을 만들어보자.

HelloWorldException 예제

package study.moon.Atest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String inputText = br.readLine();
        if (inputText.equals("Hello World")) {
            throw new HelloWorldException("헬로월드 작성하지 마세요");
        }
        bw.write(inputText);
        bw.flush();
        bw.close();
        br.close();
    }
}

class HelloWorldException extends RuntimeException {

    public HelloWorldException(String message) {
        super(message);
    }
}

결과

'Java > STUDY HALLE' 카테고리의 다른 글

[Java] enum  (0) 2021.01.30
[Java] 쓰레드 Thread  (0) 2021.01.20
[Java] 인터페이스  (0) 2021.01.08
[Java] 패키지  (0) 2020.12.29
[Java] 상속  (0) 2020.12.22