ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Camel][Java] 예외처리 (feat. try~catch문)
    Java/개념 정리 2020. 2. 24. 17:37

    예외처리란?

    Java에서는 프로그램 실행 중 발생하는 예외적인 상황을 처리하기위해 별도의 문법을 제공하고 있습니다. 이러한 예외상황이란 컴파일 중에 발생하는 문법적인 에러를 제외한 프로그램 실행 도중에 발생하는 문제 상황을 말합니다. 단적인 예를 하나 들면 입력값으로 자연수를 받고 싶은데 -3과 같은 정수가 입력되는 상황을 의미합니다.

     우리는 종종 예외를 처리하기 위해 우리는 if문을 사용해왔습니다. 하지만 if문의 경우 예외처리 이외의 용도로도 사용이 되기때문에 해당 if문이 예외처리를 위한 코드인지 구별하기가 어렵습니다. 이러한 불편함을 해결하기 위해  try~catch 기반의 예외처리 방식이 사용되는 것입니다.

     

    try ~ catch문

    인터넷 상에서 Java로 작성된 코드를 보다가 보면 try~catch 문이 사용된 예시를 본 경험이 있을 것입니다. 우리가 봐온 try~catch문은 아래의 코드처럼 구성되어 있습니다. 

    try {
    	System.out.println("입력된 두 정수의 나눗셈 결과값 : "+ (num/num2) );
    } catch ( ArithmeticException e ) { 	// ArithmeticException이 발생할 경우 진입. 
    	System.out.println("나눗셈 불가능!");
        	System.out.println(e.getMessage());
    }

     try영역에서 연산을 진행하면서 num과 num2값을 통해 나눗셈이 불가능한 값이 주어진 경우 Java 의 가상버신이 문제를 확인하게 됩니다. 그러면서 ArithmeticException 클래스의 인스턴스를 생성하고, 생성된 인스턴스의 참조 값을 catch영역에 선언된 매개변수에 전달하면서 catch문으로 진입하게됩니다. 

     

     try영역에서 예외상황이 발생하게 되면 예외상황이 발생한 문장의 나머지 부분을 건너뛰게 됩니다. 그렇기 때문에 try영역의 구성또한 신경 써줘야합니다. 

    // 잘못된 try~catch문의 사용
    try {
    	int num = num1/num2;
    	
    } catch ( ArithmeticException e ) { 	// ArithmeticException이 발생할 경우 진입. 
    	System.out.println("나눗셈 불가능!");
        	System.out.println(e.getMessage());
    }
    
    System.out.println("입력된 두 정수의 나눗셈 결과값 : "+ num );

    위의 코드에서 나눗셈의 결과값을 출력하는 System.out.println 메소드 호출이 앞서 언급한 신경 써줘야 하는 부분에 속합니다. 예외상황이 발생한다면 나눗셈의 결과값을 출력하면 않되기 때문입니다. 나눗셈을 결과를 출력하는 println 메소드는 try영역으로 이동되어야 하는 것입니다. 아래의 코드처럼 말이죠.

    //올바른 try~catch문의 사용
    try {
    	int num = num1/num2;
        	System.out.println("입력된 두 정수의 나눗셈 결과값 : "+ num );
    	
    } catch ( ArithmeticException e ) { 	// ArithmeticException이 발생할 경우 진입. 
    	System.out.println("나눗셈 불가능!");
        	System.out.println(e.getMessage());
    }
    
    

     

    e.getMessage() 는 무엇인가?

     앞서 설명한 코드들을 확인하면 e.getMessage() 메소드를 확인할 수 있습니다. ArithmeticExection의 참조변수 e를 통해 getMessga() 메소드를 호출하고 있는 것입니다. 이 메소드는 예외상황이 발생한 이유를 담은 문자열을 반환하는 메소드입니다. 이 메소드는 모든 예외 클래스들이 상속하는 Throwable 클래스에 정의되어 있는 메소드입니다.

     

    예외상황을 알리는 클래스

     프로그램을 실행하면서 여러 종류의 예외상황이 발생할 수 있습니다. 그렇기에 Java에는 이미 약속되어있는 예외상황이 존재하고 이러한 상황을 알리기 위한 클래스들도 정의되어 있습니다. 앞서 설명한 ArithmeticExection 클래스도 그 한 종류인 것입니다. 이러한 예외상황을 알리는 클래스의 대표적인 몇가지 예시는 다음과 같습니다. 

     

    ArrayIndexOutOfBoundExection - 배열의 접근에 잘못된 인덱스 값을 사용하는 경우

    ClassCastException - 허용하지 않는 형변환 연산을 진행할 경우

    NegativeArraySizeException - 배열선언 과정에서 배열의 크기를 음수로 지정하는 경우

    NullPointerException - 참조변수가 null로 초기화 된 상황에서 메소드를 호출하는 경우

     

    예외상황의 발생에 관계없이 항상 실행되는 영역 ( finally )

     말그대로 예외상황의 발생여부에 관계없이 항상 실행되는 영역을 구현할 때 finally영역을 활용합니다. 즉, finally영역은 try 영역으로 진입하면 항상 실행되는 영역인 것입니다. 중간에 return을 통해 메소드를 빠져나가려해도 finally 영역이 실행되고 난 후 메소드를 빠져나가게 됩니다. 

    try {
    	int num = num1/num2;
        	System.out.println("입력된 두 정수의 나눗셈 결과값 : "+ num );
    	return true;
    } catch ( ArithmeticException e ) { 	// ArithmeticException이 발생할 경우 진입. 
    	System.out.println("나눗셈 불가능!");
        	System.out.println(e.getMessage());
            return false;
    } finally {
    	System.out.println("finally 영역입니다.");
    }
    
    

     

    예외클래스의 정의

    그렇다면 약속된 예외상황 외의 예외를 처리하고 싶다면 어떻게 해야할까? 이전에 설명한 0으로 나눗셈을 시도하는 경우 발생하는 예외는 이미 약속된 예외상황이라고 할 수 있습니다. 그러나 자연수를 입력받고 싶은데 음수를 입력하는 경우는 프로그램의 논리에만 어긋나는 상황이므로 가상머신이 예외상황으로 인신하지 못합니다. 우리는 이러한 예외클래스를 직접 정의함으로써 예외처리를 할 수 있습니다. 

     

     그렇다면 예외 클래스는 어떻게 정의하는 것일까?

     

     예외클래스를 정의하기 위해서는 우선 Exception 클래스를 상속해야합니다. Exception 클래스는 모든 예외 클래스가 상속하는 Throwable 클래스의 하위 클래스입니다. 이러한 Exception 클래스를 상속한 클래스는 예외클래스가 되어 trt~catch문에서 활용가능한 클래스가 되는 것입니다. 

    class InvalidValueException extends Exception() {
    	public InvalidValueException() {
        	super("유효하지 않는 값입니다.");
        }
    }

     위와 같이 예외 클래스를 정의할 때, 상황의 설명에 필요한 문자열을 Exception 클래스의 생성자에 전달하면 됩니다. 이렇게 정해진 문자열은 getMessage() 메소드를 통해 반환될 수 있습니다. 

     

    throw, throws란 무엇인가?

     우선은 설명에 앞서 throw가 사용된 코드의 예시를 확인하겠습니다. 

    import java.util.Scanner;
    
    class InvalidValueException extends Exception() {
    	public InvalidValueException() {
        	super("유효하지 않는 값입니다.");
        }
    }
    
    class NewException {
    	public static void main ( String[] args ) {
        	System.out.print("How old are you?");
            try {
            	int age = getAge();
                System.out.println("I`m "+age+"-years-old.");
            } catch (InvalidValueException e) {
            	System.out.println(e.getMessage());
            }
        }
        
        public static int getAge() throws InvalidValueException {
        	Scanner sc = new Scanner(System.in);
            int age = sc.nextInt();
            if (age < 0) {
            	InvalidValueException exception = new InvalidValueException();
                throw exception;
            }
            return age;
        }
    }

     throw가 존재하는 위치는 예외상황이 발생했음을 알리는 위치입니다. 예외처리 시에 throw가 존재하는 메소드를 호출한 영역으로 넘져기게 되기 때문에 getAge()를 호출하는 구문은 try~catch문으로 감싸지게 됩니다. 예외 상황을 알리기위해 생성된 InvalidValueException 인스턴스의 참조값이 main 메소드에 존재하는 catch 영역으로 전달되는 것입니다. 

     여기서 중요한 점은 예외상황이 메소드 내에서 처리되지 않는다면, 메소드를 호출한 영역으로 예외처리가 전달됨을 throws InvalidValueException를 사용해 명시해줘야 한다는 것입니다.

     이 것은 getAge() 메소드에서 InvalidValueException의 예외상황을 처리하지 않고 getAge() 메소드가 호출되는 영역에서 InvalidValueException에 대한 처리를 해야함을 의미합니다. 이렇게 throw를 통해 전달된 예외상황은 반드시 try~catch문에 의해 처리되거나 throws에 의해 전달되어야합니다. 이말은 즉 main 메소드에서도 예외처리를 하지 않는 다면 main 메소드에서 throws 처리를 해줘야함을 의미합니다. 결과적으로 main 메소드를 호출하는 영역은 가상머신이기 때문에 만약 main 메소드에서 throws를 통해 예외상황을 전달한다면 가상머신에게 전달되게 됩니다. 

     

    댓글

Camel`s Tistory.