자바(Java)

[자바] 자바 예외처리

Ash_jisu 2023. 9. 14. 16:03

자바에서 예외 처리 방법

try, catch, finally 

 

try-catch

프로그램 실행 도중 발생된 에러는 어쩔 수 없지만 이외엔 대비처리를 해야한다.

이럴때 이제 대비 처리하는 try-catch문을 작성해줘야한다.

 

     try{
            //예외 발생 가능성이 있는 코드
     } catch(Exception1 e1){
			//Exception1 이를 처리하기 위한 코드
     } catch(Exception2 e2) {
          //Exception2가 발생시 이를처리하기 위한 코드
     }

 

예시로 문자열을 정수형으로 변환하는 과정중에 발생하는 NumberFormatException 예외를 생각했지만 자바 문서 찾아보니 RuntimeException클래스의 자식이라 try~catch문 예시로 안좋을것같다. 그래서 RuntimeException예외가 아닌 Exception예시중 FileNotFoundException이 발생할 수 있는 파일을 읽어서 가져오는 예시를 준비했다.

 

예시

public class Main {
    public static void main(String[] args) {
        BufferedReader reader = null;
        reader = new BufferedReader(new FileReader("test.txt"));
        String line = reader.readLine();
    }
}

오류 발생

 

FileNotFoundException 오류 문서를 찾아보자

Exception - IOException - FileNotFoundException 으로 연결 된것을 알수있다

문서 밑에 내용을 더 자세히 보면 아래 과정을 통해 에레메시지가 전달 되는 것을 알 수 있다

 

그렇다면 이러한 예외로 인해 프로그램 종료가 되지 않을려면 어떡해야할까

바로 catch를 쓰면 된다

public class Main {
    public static void main(String[] args) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("text.txt"));
            String line = reader.readLine();
        } catch (FileNotFoundException e1) {
            System.err.println("파일을 찾는 동안 오류가 발생했습니다1: " + e1.getMessage());
        } catch (IOException e2){
            System.err.println("파일을 읽는 동안 오류가 발생했습니다2: " + e2.getMessage());
        }
    }
}

결과

 

getMessage와 printStackTrace()

  • get Message(): 발생한 예외 클래스의 인스턴스 저장된 메세지 얻기 가능하다
  • printStackeTrace():  호출스택에 있었던 메서드의 정보와 예외 메세지 출력한다

 

멀티 catch

자바 7부터 사용가능하다

JDK 1.7부터 여러 catch블럭을 '|'기호를 이용해서 하나의 catch블럭으로 하나로 합칠수 있게 되었다. 

중복된 코드를 줄일 수 있다는 장점이 있고 이를 쓸때 하나의 catch블럭에 '|'을 통해 연결하면 된다

예시는 다음과 같다.

      try {
            //예외 발생 가능성이 있는 코드
        } catch (FileNotFoundException |NullPointerException e){
            e.getMessage();
        }

※주의사항

그 서로간에 관계가 조상과 자손의 관계면 안된다

 

 

주의점

  • 주의해야할 점은 예외 변수 이름을 다르게 해줘야한다. 왜냐면 같은 참조변수 사용불가이기 때문이다.
  • 추가로 try나 catch문은 괄호 생략 불가이다
  • 에러 출력문 관련해서 getMessage() 말고 printStackTrace() 메서드 사용법도 있지만
    운영할 시스템에 적용시 엄청난 양의 로그가 쌓여서 필요할때만 써야한다
  • 추가로 주의점은 아니고 알아두면 좋은게 이제 모든 예외 클래스는 Exception 클래스의 자손이므로 
    Exception 클래스 타입의 참조변수를 선언해 놓으면 어떤 예외가 발생해도 이 catch 블럭에 의해서
    처리 된다

 

throw

throw 키워드는 예외를 직접 발생시키는데 사용된다. 즉, 예외 객체를 생성하고 해당 예외를
프로그램으로 던진다. 사용이유는 특정 조건이나 상황에서 예외가 발생할때 개발자가 짠 코드가

오류 발생시에도 문제 없이 잘 돌아가는지를 확인하기 위해서 이다. 사용방법은 아래와 같다

public class Main {
    public static void main(String[] args) {
        BufferedReader reader = null;
        try {
        	//고의적으로 FileNotFoundException 오류 발생
            throw new FileNotFoundException("예외 메시지");
        } catch (FileNotFoundException e) {
            System.err.println("파일을 찾는 동안 오류가 발생했습니다: " + e.getMessage());
        } 
    }
}

 

throws

해당 메서드가 어떤 예외를 던질 수 있는지를 선언하다. 이것을 통해 컴파일러와 

다른 프로그래머에게 메서드에서 발생할 수 있는 예외에 대한 정보를 제공한다.

이를 통해 컴파일러는 해당 메서드를 사용하는 코드에서 예외를 처리하도록 요구한다.

 

사용법은 다음과 같다

public void readFile() throws IOException {
    // 파일을 읽는 코드
}

따라서 개발자는 해당 메서드를 사용할때 예외 처리 코드를 참고하여 짤 수 있다.(throwsException()를

메서드 선언부에 선언해놓으면 try-catch블록 사용안할시 컴파일 에러 발생하기때문에 사실상

예외처리 꼭 쓰라는뜻이다)

 

finally

finally블럭은 예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용된다.
try-catch문의 끝에 선택적으로 붙여서 사용하며 try-cath-finally순으로 구성된다

  try{
        // 예외가 발생할 가능성이 있는 문장들을 넣는다
    }catch(Exception e){
        //예외 처리를 위한 문장을 적는다
    }
    finally{
        //try-catch 마지막에 위치해야함
        //finally 블럭은 try-catch문의 맨 마지막에 위치해야한다
    }

코드 참고: 자바의 정석 

 

예외 발생시: try -> catch -> finally 순

예외 발생하지 않을시: try -> finally순

 

 

try-with-resource

try-with-resource문은 괄호()안에 객체를 생성하는 문장을 넣으면,  jdk1.7로부터 추가된 try-catch문으로 사용후 꼭 닫아줘야하는 것들을 처리해준다.

자원이 반환되기 때문인데 이때 자원반환은 메모리반환의 의미가 아니다(메모리 관련된건 가비지 컬렉터에서 처리). 찾아보니 I/O입출력을 사용할때 파일 디스크립터, 파일 또는 다른 I/O리소스를 식별하는데 사용되는 것이다. 좀더 자세히 설명하면 정수 형태의 고유한 식별자로, 커널 내부에서 열린 파일, 소켓, 파이프, 디바이스 및 기타 I/O리소스를 추적고 관리하는데 사용되는것이 있는데 이것의 사용을 멈춤으로써 시스템 리소스를 효율적으로 관리한다.

 

추가로 try괄호안에 넣으면 try문 나갈때 자동으로 close()해준다

    try(FileInputStream fis = new FileInputStream("score.dat"); DataInputStream dis = new DataInputStream(fis)){
        while(true){
            score = dis.readInt();
            System.out.println(score);
            sum += score;
        }
    }catch (EOFException e){
        System.out.println("점수의 총합은 "+sum+"입니다.");
    }catch(IOException ie){
        ie.printStackTrace();
    }

코드 참고: 자바의 정석 

 


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

아래 계층 구조는 자바에서 실행시 발생할 수 있는 오류(Exception &Error)를 클래스로 정의하고 있다.

Throwalbe은 모든 java클래스들이 그렇듯 Object를 암시적으로 상속받고있다. 그래서 아래와 같이 따로 명시되어있지않다.

 

extends Object가 따로 명시되어있지않은 모습

 

 

 

자바와 Exception은 RuntimeExcxeption과 RuntimeException을 제외한 Exception클래스의 자식클래스 2개로 구분할 수 있는데 이건 밑에서 더 자세히 다루겠다.

 

 

 


RuntimeException과 RE가 아닌것의 차이

위 그림에서 파란색 점선 영역이 CheckedException으로 컴파일러(빌드) 단계에서 검출되고

노란색 영역이 UnCheckedException으로 런타임(JVM구동)시 예외가 발생한다. 

 

RuntimeException

  • RuntimeException클래스를 상속받는 자식 클래스들은 주로 치명적인 예외상황을 발생시키지 않는
    예외들로 구성되어있다
  • try/ catch문을 사용하기보다는 프로그램을 작성하면서 예외가 발생하지 않도록 주의하는 것이 좋다
    (ex. null인 문자열 text변수의 길이 호출, text.lenght() 호출하면 NullPointerException발생 
    따라서 사용자가 프로그램을 잘 작성하자)

 

 

ChekcedException

  • CheckedException 클래스인 Exception 클래스에 속하는 자식 클래스들은 치명적인 예외상황을
    발생 시키기 때문에 반드시 try / catch문을 사용하여 예외처리를 해야한다
  • 자바 컴파일러는 RuntimeException 클래스의 자식 클래스에 속하는 예외가 발생할 가능성이 있는
    구문에는 반드시 예외를 처리하도록 강제하고 있다.
  • 위에도 언급했지만 컴파일 단계에서 확인한다

 

 

 

 


Exception과 Error의 차이

이 2개의 차이를 알아보기전에 먼저 Exception과 Error에 대해 알아보자.

Exception

  • 컴퓨터의 에러가 아닌 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류이다.
  • 예외 발생시 프로그램 종료된다
  • 개발자가 구현한 로직에서 발생한다
  • 쓰레드에 영향을 준다(예외 발생시 해당 쓰레드의 실행이 중단될 수도 있다)

 

Error

  • 컴퓨터의 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 이상이 생겼거나 JVM 실행에 문제가 생겼을 경우 바랭하는 것이다
  • 프로세스에 영향을 준다
  • 시스템 레벨에서 발생한다

 

차이점

  • 예외나 에러는 발생하면 프로그램이 종료가 된다는것은 동일하지만 예외
    사용자의 잘못된 조작또는 코딩으로 인해 발생하기때문에 예외처리를 통해 프로그램을
    종료되지 않고 정상적으로 작동되게 만들어 줄 수 있다. 
    에러시스템 환경이나 하드웨어를 수정하거나 개선해야한다

 

 


커스텀한 예외 만드는 방법

기존 정의된 예외 클래스 외에 필요에 따라 프로그래머가 새로운 예외 클래스를

정의하여 사용할 수 있다. Exception 클래스를 상속받거나, 필요에 따라 알맞은

예외 클래스를 상속받아 만든다. 

public class MyException {

    public static void main(String[] args) throws SpaceException {
        methodA(5);
    }

    static void methodA(int space) throws SpaceException {
        if (space < 1) {
            throw new SpaceException("공간 부족");
        }
    }
}

class SpaceException extends Exception {
    public SpaceException(String message) {
        super(message);    //조상 클래스인 Exception의 생성자 호출
    }
}

 

예외 되던지기

한 메서드에서 발생할 수 있는 예외가 여러 개인 경우, 일부는 메서드 내부에서

처리하고 일부는 선언부에 지정해서 메서드를 호출한 쪽에서 처리할 수 있다. 
또, 하나의 예외에 대해서도 양쪽에서 처리하도록 할 수 있는데 이를 '예외 되던지기'라고한다.

 

사용 상황은 다음과 같다

  • 예외를 발생시킨 메서드에서 예외를 처리할 필요가 없고, 상위 호출자에게 예외 처리를 위임하고자 할때 사용
  • 예외를 발생시킨 위치에서 예외에 대한 정보를 보존하면서 다른 메서드로 예외를 전달하고자 할때 사용

 

 

 


참고

자바의 정석(저자 남궁성)

-Exception 전반적인 내용

https://wisdom-and-record.tistory.com/46

-예외 계층과 RunitimeException, RE의 차이점

https://sujl95.tistory.com/62