본문 바로가기
생활코딩/JAVA

생활코딩 - JAVA (예외 (exception))

by Love of fate 2020. 12. 29.
728x90
반응형

[예외란 무엇인가]

예외란 프로그램을 제작하는 과정에서 발생하는 오류를 제어 또는 처리하는 것이다.

예외를 이해하려면 기본적으로 알고 있어야 할 것들이 많이 있다. 

 

애플리케이션을 잘 만들고 잘 동작하게 하며, 애플리케이션을 규모 가변성 있게 구축하는 것은 예외와 상관이 없거나 적다는 것이다. 

예외, 보안과 같은 주제는 실패하지 않는 법이라고 할 수 있다.

 

일반적으로 오류 또는 에러라는 많이 쓰는데, 자바를 비롯한 여러 프로그래밍 언어에서는 예외라는 표현을 쓴다.

오류는 예외의 일종이라고 생각하면 된다.

일반적이지 않은 상황에서 우리가 기획했던 바와 다르게 발생하는 문제를 포괄적으로 예외(exception)라고 한다.

또한 이 같은 예외 상황에서 해당 프로그램을 만든 프로그래머가 예외 상황을 처리하고 관리할 수 있도록 돕는 방법이나 언어 차원에서 제공하는 방법들도 예외 또는 예외 처리라고한다.

 

[divide() 메소드를 추가한 계산기]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.opentutorials.javatutorials.exception;
class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
        System.out.print("계산결과는 ");
        System.out.print(this.left/this.right);
        System.out.print(" 입니다.");
    }
public class CalculatorDemo {
    public static void main(String[] args) {
        Calculator c1 = new Calculator();
        c1.setOprands(100);
        c1.divide();
    }
}

10을 0으로 나누는 것은 수학적으로 의미가 없기 때문에 일반적으로 프로그래밍 언어에서는 이를 에러로 처리하거나 NaN(Not a Number)이라는 특수한 형태의 데이터를 반환하는 식으로 비정상적인 상황임을 알려준다.

그리고 자바는 어떤 숫자를 0으로 나누려고 할 때 에러를 발생시키는 기본적인 동작 방법을 지닌 언어이다.

예제를 실행하면 아래와 같은 결과가 출력된다.

1
2
3
4
계산결과는 Exception in thread "main" java.lang.ArithmeticException: / by zero
    at org.opentutorials.javatutorials.exception.Calculator.divide(CalculatorDemo.java:10)
    at org.opentutorials.javatutorials.exception.CalculatorDemo.main(CalculatorDemo.java:18)
 

수학적인 예외가 발생했다는 뜻이다.

at 부분은 ArithmeticExceiption이 어디서 발생했는지 추적할 수 있게 도와주는 내용이다. 

메소드를 호출할 경우 메소드 안에서 다른 메소드를 호출하고, 또 그 메소드 안에서 다른 메소드를 호출할 수도 있는데, 그런 관계를 추적할 수 있게 해주는 것이다.

 

[divide() 메소드에 try/catch를 적용]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.opentutorials.javatutorials.exception;
class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
        try {
            System.out.print("계산결과는 ");
            System.out.print(this.left/this.right);
            System.out.print(" 입니다.");
        } catch(Exception e){
            System.out.println("오류가 발생했습니다 : "+e.getMessage());
        }
    }
public class CalculatorDemo {
    public static void main(String[] args) {
        Calculator c1 = new Calculator();
        c1.setOprands(100);
        c1.divide();
         
        Calculator c2 = new Calculator();
        c2.setOprands(105);
        c2.divide();
    }
}

try는 '시도한다'라는  뜻으로 로직을 실행해본다라는 뜻이고 catch는 '잡는다'라는 뜻이다.

1

2

계산결과는 오류가 발생했습니다 : / by zero

계산결과는 2 입니다.

즉 10행은 실행이 되었지만 11행은 실행되지 않았다. 그런데 이전 예제와 다른 점이 있다. 오류가 발생하지 않았다는 점이다. 마치 정상적인 에플리케이션인 것처럼 동작하고 있다. 그 이유는 try...catch 때문이다.

 

try와 catch는 예외를 처리하기 위한 핵심적인 문법 요소이다.

try 안에 있는 코드가 실행되다가 에러가 발생하면 그 순간 try 안의 내용은 실행이 중지된다. 그리고 catch 블록에 들어있는 구문이 실행되기 시작한다. 그렇기 때문에 예제에서는 오류가 발생했다는 메시지가 출력되는 것이다.

자바 가상 머신이 프로그램을 실행하는 과정에서 try블록 내에서 에러가 발생하면 자동으로 catch 구문을 찾는다.

그 구문의 매개변수로 에러 정보가 담긴 객체를 매개변수로 전달한다. 그 객체의 데이터 타입은 Exception이다.

 

try에는 예외 발생이 예상되는 로직을 위치시키고 try로 감싸는 것이다. catch에는 예외가 발생했을 때 그 예외를 뒷수습하는 로직이 들어간다.

[뒷수습]

[divide() 메소드의 catch 블록에서 예외를 처리]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.opentutorials.javatutorials.exception;
class Calculator{
    int left, right;
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void divide(){
        try {
            System.out.print("계산결과는 ");
            System.out.print(this.left/this.right);
            System.out.print(" 입니다.");
        } catch(Exception e){
            System.out.println("\n\ne.getMessage()\n"+e.getMessage());
            System.out.println("\n\ne.toString()\n"+e.toString());
            System.out.println("\n\ne.printStackTrace()");
            e.printStackTrace();
        }
    }
public class CalculatorDemo {
    public static void main(String[] args) {
        Calculator c1 = new Calculator();
        c1.setOprands(100);
        c1.divide();
    }
}

[출력 결과]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
계산결과는 
 
e.getMessage()
/ by zero
 
 
e.toString()
java.lang.ArithmeticException: / by zero
 
 
e.printStackTrace()
java.lang.ArithmeticException: / by zero
    at org.opentutorials.javatutorials.exception.Calculator.divide(CalculatorDemo.java:11)
    at org.opentutorials.javatutorials.exception.CalculatorDemo.main(CalculatorDemo.java:25)
 

printStackTrace() 메소드는 문자열을 반환하는 것이 아니라 printStackTrace() 내부적으로 화면에 에러 상황을 출력하기 때문에 위의 실행 결과에서 나타나는 에러 메시지가 출력된다. 여기서는 어떤 Exception이 발생하고 또 Exception이 발새한 원인은 무엇이고 어떤 로직을 통해 에러가 발생했는지에 관한 정보를 보여준다.

catch 안의 구문은 위에서 아래로 갈수록 더 상세한 정보를 제공한다.

catch 밖에 코드를 작성하면 catch블록에 있는 내용이 실행되고 메소드 동작이 중단되는 것이 아니라 catch 바깥으로 나와서 나머지 로직을 계속해서 실행시킨다.

[다양한 예외와 다중 캐치]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.opentutorials.javatutorials.exception;
 
class A{
    private int[] arr = new int[3];
    A(){
        arr[0]=0;
        arr[1]=10;
        arr[2]=20;
    }
    public void z(int first, int second){
        try {
            System.out.println(arr[first] / arr[second]);
        } catch(ArrayIndexOutOfBoundsException e){
            System.out.println("ArrayIndexOutOfBoundsException");
        } catch(ArithmeticException e){
            System.out.println("ArithmeticException");
        } catch(Exception e){
            System.out.println("Exception");
        }
         
    }
}
 
public class ExceptionDemo1 {
    public static void main(String[] args) {
        A a = new A();
        a.z(100);
        a.z(10);
        a.z(21);
    }
}

[출력 결과]

1

2

3

ArrayIndexOutOfBoundsException

ArithmeticException

2

try에서 예외가 발생하면 catch부분이 실행돼서 뒷수습을 할 수 있게 해주는데, try 안에서 발생한 예외가 ArithmeticException이라면 그에 해당하는 catch가 실행되고, 마찬가지로 ArrayIndexOutOfBoundsException 예외가 발생하면 그에 해당하는 catch가 실행되는 것이다.

 

즉, try에서 발생한 문제가 무엇이냐에 따라 각 catch에서 실행될 수 있게 분기하는 역할을 하는 것이 catch를 중첩해서 사용하는 다중 catch이다. 경우에 따라서는 두 가지예외 외에 다른 예외가 발생할 수 있다. 이를 방지하고 싶을 떄는 Exception을 넣으면 된다. 

이 같은 기능이 있기 때문에 어떤 문제가 발생했을 때 그 문제에 더 적합한 해법을 선택할 수 있다는 것이 자바 언어가 제공하는 서비스 라고 할 수 있다.

 

Exception예외를 포착하는 catch 블록을 맨 위에 둔 경우 이클립스가 다음과 같이 경고한다.

Exception이 다른 예외보다 먼저 나왔다는 뜻이다. Exception은 모든 예외를 포괄하는 예외이기 때문에 Exception에 대한 catch 다음에 다른 예외가 나타나더라도 Exception에서 실행이 끝나기 때문에 그러한 예외를 잡는 코드는 절대로 실행될 수 없는 코드가 된다. Exception은 가장 포괄적인 예외라서 그것이  먼저 시도되면 그 다음에 있는 특정 상황에서 사용되는 예외는 실행될 기회조차 얻지 못하는 것이다. 

 

이런 문제를 미리 파악하지 못한다면 실제로 서비스되고 있는 동안에 자신도 모르게 어떤 문제점에 계속해서 노출될 수도 있다. 결국 컴파일러가 제공하는 이러한 성가신 기능들은 충분히 비용을 지불할 만한, 또 컴파일러가 제공하는 중요한 서비스라고 생각해야 한다.

[finally]

finally절은 try/catch와 함꼐 올 수 있으며, 언제나 try/catch 뒤에 나와야 하고, 다른 위치에서 등장하면 문법오류가 발생한다. try에서 문제가 발생해서 catch의 내용이 실행되거나 try에서 아무런 문제가 발생하지 않은 경우에도 finally 영역의 로직이 실행된다.

즉, finally는 try/catch 문의 결과에는 무관하게 언제나 실행되도록약속된 로직이다.

 

finally는 구문이 정상적이건 또는 비정상적이라서 예외가 발생했건 간에 언제나 finally 절이 실행된다는 것을 의미한다.

 

어떤 경우든 자바 애플리케이션에서 반드시 해야 할 일은 자바 애플리케이션과 데이터베이스 사이에 이뤄진 연결을 끊는 것이다. 

예외가 발생할 수 있는 어떤 의심되는 로직에서 예외가 발생하거나 발생하지 않아도 언제나 반드시 처리해야 할 일이 있다면 그 일을 finally에 두면 된다. 

예외는정상적인 처리 또는 애플리케이션을 개발하는 개발자가 상정한 정상적인 프로세스에서 벗어나는 문제가 발생했을 때 그 문제를 해결하는 체계라고 볼 수 있다.

728x90
반응형

'생활코딩 > JAVA' 카테고리의 다른 글

생활코딩 - JAVA (Object 클래스)  (0) 2020.12.31
생활코딩 - JAVA (예외 던지기)  (0) 2020.12.30
생활코딩 - JAVA (다형성)  (0) 2020.12.29
생활코딩 - JAVA (인터페이스)  (0) 2020.12.19
생활코딩 - JAVA (final)  (0) 2020.12.19