Language/Java

5. 예외처리

리져니 2021. 7. 14. 20:48

프로그램이 실행 중 어떤 원인에 의해서 오작동 하거나 비정상적으로 종료되는 경우, 이러한 결과를 초래하는 원인을 프로그램 에러 또는 오류 라고 한다.

 

 

에러 종류

컴파일 에러: 컴파일시 발생하는 에러

런타임 에러: 실행시 발생하는 에러

논리적 에러: 실행은 되지만, 의도와 다르게 동작하는 것

 

컴파일을 에러 없이 성공적으로 마쳤다고 해도 프로그램의 실행 시에도 에러가 발생하지 않는 것은 아님.

컴파일러가 실행 도중에 발생할 수 있는 잠재적 오류까지 검사할수 없기 때문.

자바에서는 실행 시(runtime) 발생할 수 있는 프로그램 오류를 애러(error)와 예외(exceptio)으로 구분한다.

예외 클래스 계층도

Error : 프로그램 코드에 의해서 수습될수 없는 오류 ex)메모리 부족, 스택오버플로우

Exception: 프로그램 코드에 의해서 수습될수 있는 오류

- Runtime Exception 클래스들: 프로그래머의 실수로 발생하는 예외. ex) 배열의 범위를 벗어남

- 다른 Exception클래스들: 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외. ex) 존재하지 않는 파일의 이름 입력

 

 

예외 처리(try-catch 문)

예외의 발생으로 인한 실행 중인 프로그램의 갑작스런 비정상 종료를 막고, 정상적인 실행상태를 유지할 수 있도록 하는것.

* 발생한 예외를 처리하지 못하면, 프로그램은 비정상적으로 종료되며 처리되지 못한 예외는 JVM의 예외처리기가 받아서 예외의 원인을 화면에 출력한다.

try-catch문 구조

try블럭 내에서 예외가 발생한 경우,

발생한 예외와 일치하는 catch문이 있는지 확인하여 해당 cath문 블럭 내의 문장들을 수행하고, 전체 try-catch문을 빠져나가서 그 다음 문장을 계속해서 수행한다. 만일 일치하는 catch블럭을 찾지 못하면 예외는 처리되지 못한다.

 

try블럭 내에서 예외가 발생하지 않은 경우,

catch블럭을 거치지 않고 전체 try-catch문을 빠져나가서 수행을 계속한다.

 

class ExceptionTest{
	public static void main(String[] args){
    	int num = 100;
        int result = 10;
        
        for(int i=0;i<10;i++){
        	try{
            	result = num / (int) (Math.random() * 10);
                System.out.println(result);
             }catch ( ArithmeticException e){
             	System.out.println("0");
             }
         }
     }
 }

100을 0부터 9까지의 수중에 랜덤한 숫자로 나눈 결과값을 10번 출력하는 소스코드이다.

이때 try-catch문을 봐보면, try의 코드블럭에서 결과값을 구한뒤 출력하고, catch문에서는 try문을 실행할때ArtihmeticException가 발생하면 실되는 코드 블럭을 작성했다.

ArtihmeticException가 발생할수 있는 경우는 산술연산과정에서 오류가 있을때 발생하는 예외이며, 정수를 0으로 나누는 것이 금지되어있기 때문에 작성한 try문에서 발생할수 있는 예외이다.

 

이 소스 코드를 실행하면, result가 100/0 연산의 결과값이 되었을때 발생하는 ArtihmeticException예외를 catch문에서 처리해 줌으로써  프로그램 실행도중에 예외가 발생하더라도 비정상적으로 종료되지 않도록("0"을 화면에 출력) 했다.

 

* catch(Exception e) 로 바꾸어도 정상작동이 되는데, 그 이유는 Exception 클래스가 ArtihmeticException의 조상 클래스 이기 때문이다

 

 

printStackTrace(), getMessage()

예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨 있으며, printStackTrace(), getMessage()를 통해 이 정보들을 얻을 수 있다.

- printStackTrace() : 예외발생 당시의 호출스택에 있었던 메서드의 정보와 예외 메세지를 화면에 출력

- getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메세지 출력

 

실행 결과

멀티 catch 블럭

여러 catch블럭을 '|'기호를 이용해서 하나의 catch블럭으로 합칠수 있다. (예외 클래스의 개수에는 제한이 없다)

 

그러나, '|'기호로 연결된 예외 클래스가 조상과 자손의 관계에 있다면 컴파일 에러가 발생한다. 왜냐면 상속관계에 있는 두 클래스를 같이 써주는것과 조상클래스 하나만 catch문에 써주는 것이 같이 때문이다.

(불필요한 코드는 제거하라는 의미에서 에러가 발생한것)

 

 

finally 블럭

예외의 발생여부에 상관없이 실행되어야할 코드를 포함시킬 목적으로 사용.

try-catch문의 끝에 선택적으로 덧붙여 사용.

try-catch-finally

예외가 발생한 경우 'try-catch-finally' 순서로 실행,

예외가 발생하지 않은 경우 'try-finally'순으로 실행됨

 

 

자동 자원 반환 (try-with-resource문)

입출력에 사용되는 클래스 중에서 자원 반환을 위해 사용한 후에 꼭 닫아줘야 하는 것들이 있다. 이런 자원 반환을 자동으로 해주는 용도로 사용할수 있다.

작업 중에 예외가 발생해도 dis가 닫히도록 finally블록에 dis.close()를 적어야 한다.

그러나 close()가 예외를 발생시킬수 있기 때문에 왼쪽 코드와 같이 finallt문 안에 try-catch 예외문을 추가하여 적어야만 한다.

코드가 더 복잡해지고 try블럭과 finally블럭에서 모두 예외가 발생하면 try블럭의 예외가 무시된다.

 

이를 개선하여 try-with-resorce문으로 바꾸면,

try-with-resorce문의 괄호()안에 객체를 생성하는 문장을 넣어 해당 객체를 따로 close()를 호출하지 않아도 try블럭을 벗어나는 순간 자동적으로 close()가 호출된다

 

* try-with-resorce문을 사용하려면 클래스가 AutoClosable이라는 인터페이스를 구현한 것이여야 한다.

public interface AutoClosable{
	void close() throws Exception{};
}

 

728x90