Part9 예외처리

Exception

프로그램 실행중 예기치 못한 사건을 예외라고 한다. 예외 상황을 미리 예측하고 처리할 수 있는데, 이렇게 하는 것을 예외 처리라고 한다.

    public class ExceptionExam {
        public static void main(String[] args) {
            int i = 10;
            int j = 5;
            int k = i / j;
            System.out.println(k);
            System.out.println(main 종료!!);
        }
    }
  • 위 코드에서 j를 0으로 바꾸면 Exception 발생
    • j를 0으로 바꾸면 Arithmetic Exception이 발생하면서 프로그램이 종료된다.
    • Java는 정수를 정수로 나눌 때 0으로 나누면 오류가 발생한다.
  • 예외 처리
    • 프로그래머는 j라는 변수에 0이 들어올지도 모르는 예외 상황을 미리 예측하고 처리할 수 있다.
  • 예외 처리하는 문법
    • 오류가 발생할 예상 부분을 try라는 블록으로 감싼 후, 발생할 오류와 관련된 Exception을 catch라는 블록에서 처리한다.
    • 오류가 발생했든 안했든 무조건 실행되는 finally라는 블록을 가질 수 있다.
    • finally 블록은 생략가능하다.
    public class ExceptionExam {
        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            try{
                int k = i / j;
                System.out.println(k);
            }catch(ArithmeticException e){
                System.out.println("0으로 나눌 수 없습니다. : " + e.toString());
            }finally {
                System.out.println("오류가 발생하든 안하든 무조건 실행되는 블록입니다.");
            }
        }
    }
  • 실행결과
    • 0으로 나눌 수 없습니다. :java.lang.ArithmeticException:/by zero
      오류가 발생하든 안하든 무조건 실행되는 블록입니다.
  • Exception 처리하지 않았을 때는 프로그램이 강제 종료되었는데 catch라는 블록으로 Exception을 처리하니 강제종료되지 않고 잘 실행되는 것을 알 수 있다.
  • try 블록에서 여러종류의 Exception이 발생한다면 catch라는 블록을 여러개 둘 수 있다.
  • Exception 클래스들은 모두 Exception 클래스를 상속받으므로, 예외 클래스에 Exception을 두게 되면 어떤 오류가 발생하든지 간에 하나의 catch 블록에서 모든 오류를 처리 할 수 있다.

Throws

throws는 예외가 발생했을 때 예외를 호출한 쪽에서 처리하도록 던져준다.

    public class ExceptionExam2 {   
        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            int k = divide(i, j);
            System.out.println(k);
        }

        public static int divide(int i, int j){
            int k = i / j;
            return k;
        }
    }

정수를 매개변수로 2개를 받아들인 후 나눗셈을 한 후 그 결과를 반환하는 divide 메소드
main 메소드에서는 divide 메소드를 호출

다음과 같이 divide 메소드를 수정

    public class ExceptionExam2 {

        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            int k = divide(i, j);
            System.out.println(k);
        }

        public static int divide(int i, int j) throws ArithmeticException{
            int k = i / j;
            return k;
        }
    }

메소드 선언 뒤에 throws ArithmeticException이 적혀있는 것을 알 수 있다. 이렇게 적어놓으면 divide 메소드는 ArithmeticException이 발생하니 divide 메소드를 호출하는 쪽에서 오류를 처리하라는 뜻이다.

    package javaStudy;
    public class ExceptionExam2 {

        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            try{
                int k = divide(i, j);
                System.out.println(k);
            } catch(ArithmeticException e){
                System.out.println("0으로 나눌수 없습니다.");
            }

        }

        public static int divide(int i, int j) throws ArithmeticException{
            int k = i / j;
            return k;
        }

    }

Exception 발생시키기

강제로 오류를 발생시키는 throw

throw는 오류를 떠넘기는 trhows와 보통 같이 사용된다.

    public class ExceptionExam3 {   
        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            int k = divide(i, j);
            System.out.println(k);

        }       
        public static int divide(int i, int j){
            int k = i / j;
            return k;
        }   
    }
  • divide 메소드는 2번째 파라미터의 값이 0일 경우 나누기를 할 때 Exception이 발생한다.

위의 코드를 에러가 발생하지 않게 수정

    public class ExceptionExam3 {
        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            int k = divide(i, j);
            System.out.println(k);      
        }

        public static int divide(int i, int j){
            if(j == 0){
                System.out.println("2번째 매개변수는 0이면 안됩니다.");
                return 0;
            }
            int k = i / j;
            return k;
        }
    }
  • j가 0일 경우 안된다는 메시지가 출력되도록 수정하고 0을 리턴
  • 이렇게 할 경우 main 메소드의 k변수는 0값을 가지게 되고 0을 출력하게 된다.
  • 0으로 나눈 결과는 0이 아니다. 0으로 반환하면 더 큰 문제가 발생할 수도 있다.



에러도 발생하지 않고, 올바르지 않은 결과를 리턴하지 않도록 수정

    public class ExceptionExam3 {
        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            int k = divide(i, j);
            System.out.println(k);

        }       
        public static int divide(int i, int j) throws IllegalArgumentException{
            if(j == 0){
                throw new IllegalArgumentException("0으로 나눌 수 없어요.");
            }
            int k = i / j;
            return k;
        }   
    }
  • j가 0일 경우에 new연산자를 통하여 IllegalArgumentException 객체가 만들어 진다.
  • new 앞에 throw는 해당 라인에서 Exception이 발생한다는 의미이다.
  • 즉 그 줄에서 오류가 발생했다는 것이다. 0으로 나눌 수 없습니다. 라는 오류가 발생한 것이다.
  • Exception 클래스 이름을 보면 argument가 잘못되었기 때문에 발생한 오류라는 것을 알 수 있다.



divide 메소드를 호출한 쪽에서의 오류 처리

    public class ExceptionExam3 {   
        public static void main(String[] args) {
            int i = 10;
            int j = 0;
            try{
                int k = divide(i, j);
                System.out.println(k);
            }catch(IllegalArgumentException e){
                System.out.println("0으로 나누면 안됩니다.");
            }           
        }

        public static int divide(int i, int j) throws IllegalArgumentException{
            if(j == 0){
                throw new IllegalArgumentException("0으로 나눌 수 없어요.");
            }
            int k = i / j;
            return k;
        }   
    }
  • divide 메소드 뒤에 throws IllegalArgumentException은 해당 오류는 divide를 호출한 쪽에서 처리를 해야한다는 것을 의미한다.

사용자 정의 Exception

  • Exception 클래스를 상속 받아 정의한 checked Exception
    • 반드시 오류를 처리 해야만 하는 Exception
    • 예외 처리하지 않으면 컴파일 오류를 발생 시킨다.
  • RuntimeException 클래스를 상속 받아 정의한 unChecked Exception
    • 예외 처리하지 않아도 컴파일 시에는 오류를 발생시키지 않는다.



RuntimeException을 상속받은 BizException 객체

    public class BizException extends RuntimeException {
        public BizException(String msg){
            super(msg);
        }       
        public BizException(Exception ex){
            super(ex);
        }
    }



BizSerice 클래스는 업무를 처리하는 메소드를 가지고 있다고 가정한다.

    public class BizService {
        public void bizMethod(int i)throws BizException{
            System.out.println("비지니스 로직이 시작합니다.");
            if(i < 0){
                throw new BizException("매개변수 i는 0이상이어야 합니다.");
            }
            System.out.println("비지니스 로직이 종료됩니다.");
        }
    }



앞에서 만든 BizService를 이용하는 BizExam 클래스

  • 매개변수 값을 -1을 넘길 때는 Exception이 발생하기 때문에 try catch 블록으로 처리한다.
    public class BizExam {  
        public static void main(String[] args) {
            BizService biz = new BizService();
            biz.bizMethod(5);
            try{
                biz.bizMethod(-3);
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }


프로그래머스 자바 입문을 마무리하며

강의 자체는 굉장히 깔끔했다. 개념 설명 이후 실습을 진행하고, 해당 개념에 대한 의문점도 강의에서는 미리 지적하여 설명해준다. 입문 강의를 들으니 대학 2학년 때 들었던 java 강의가 생각났다. 그 때 이정도 수준의 이해도를 얻었다면 어땠을까… 라는 생각이 스쳐 지나갔다. 이제 입문 강의를 들은 것 뿐이지만, java의 j정도 알게 되었다. 인터페이스나 내부클래스 같은 개념은 실제로 사용해봐야지 더 확실하게 개념이 와닿을 것 같다.

강의를 모두 수강하고도 개념이 조금 헷갈리는 것들을 적어두고 한번 더 복습을 해야할 것 같다.

  • 인스턴스를 만들 때 Parents c = new Child(); <- Parents와 Child가 정확히 어떤 역할을 하는지
  • 인터페이스와 추상클래스의 차이점
  • 내부 클래스들 각각의 쓰임
  • 사용자 정의 Exception
  • java의 얕은 복사와 깊은 복사
  • super()
  • 클래스 형변환


해당 강의와 강의 노트는 프로그래머스 자바 입문에서 언제든지 다시 들을 수 있다!

댓글남기기