본문 바로가기

웹개발/Java

[Java] 기본이자 기본이 아닌 Exception 클래스!



Exception 

대부분 예외는 java.lang.Exception 클래스를 상속하며, 이를 확장하여 사용합니다. 
확장 사용하는 기본 형태는 아래 코드와 같습니다.
 

기본 예제

    public class MyException extends Exception 
    { 
        private static final long serialVersionUID = 1L; 

        public MyException(String ErrorMessage) 
        { 
           super(ErrorMessage); 
        } 
    }
RuntimeException 또한 Exception에서 확장되었으며, RuntimeException을 확장하는 경우 컴파일러에서 예외 처리를 강요하지 않습니다.

즉 Exception은 Try Catch 등의 예외 처리나 함수 끝에 Throws 키워드를 통해 발생할 수 있는 
예외를 정의하지 않으면 컴파일이 안되지만 RuntimeException의 경우 별개의 처리를 하지 않아도 컴파일이 됩니다.

RuntimeException VS Exception


[소스]

    

    // 1. RumtimeException을 상속받은 예

    public class RuntimeMyException extends RuntimeException {
        private static final long serialVersionUID = -2109556465938566852L;
        public RuntimeMyException(String ErrorMessage)
        {
           super(ErrorMessage);
        }
    }
    // 2. Exception을 상속받은 예    
    public class MyException extends Exception {
        private static final long serialVersionUID = 2752084085235371720L;
        public MyException(String ErrorMessage)
        {
           super(ErrorMessage);
        }
    }
    
    public void RuntimeMyException_Sample(int a, int b{
        // RuntimeException을 발생시킬 때 throws에 정의하지 않아도 동작한다.
        if(b == 0) throw new RuntimeMyException("분모는 Null일 수 없습니다.");
        System.out.println(String.format("%d / %d = %d", a, b, a/b));
    }
    
    public void MyException_Sample(int a, int b) throws MyException {
        // Exception을 발생시킬 때 throws에 정의해야 컴파일 가능하다.
        if(b == 0) throw new MyException("분모는 Null일 수 없습니다.");
        System.out.println(String.format("%d / %d = %d", a, b, a/b));
    }
    public void Run() {
        // Runtime Exception의 경우 예외처리를 하지 않아도 컴파일이 가능하다.
        RuntimeMyException_Sample(10, 0);
        try
        {
            MyException_Sample(10, 0);
        } catch (MyException e) {
            e.printStackTrace();
        }
    }



[콘솔]


 Exception in thread "main" ExceptionTest$MyException: 분모는 Null일 수 없습니다. 

 at ExceptionTest.MyException_Sample(ExceptionTest.java:49) 

 at ExceptionTest.main(ExceptionTest.java:13)




종류


Exception 상속
Exception 클래스
발생이유
부끄럽게도 null을 자꾸 소환할 때
잘못된 입력값이 있을 때
나눗셈 연산 시 분모가 0일 때
Format 형식과 값이 매칭이 안될 때
배열의 범위를 벗어나서 참조할 때

StackFlow에서 주의 깊게 봐야 하는 정보

StackFlow란 현재 실행되는 프로그램의 발자국이라고 생각하시면 될 것 같습니다.
호출에 의해 Stack이 쌓이며 오류가 발생한 위치를 정확하게 찾아주는 역할을 합니다.

아래와 같은 로그가 존재할 때 주의 깊게 봐야 하는 정보가 있습니다.
아래 로그는 StackFlow가 짧은 편이지만 보통 웹서버 환경에서는 많은 StackFlow가 호출됩니다.

제일 하단의 StackFlow에서 거슬러 올라가 최상단 위치에서 오류의 위치를 출력합니다.
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 6, Size: 6 
at java.util.ArrayList.rangeCheck(ArrayList.java:635) 
at java.util.ArrayList.get(ArrayList.java:411) 
at ExceptionTest.Exception04_IndexOutOfBoundException(ExceptionTest.java:110) 
at ExceptionTest.main(ExceptionTest.java:23)

위 로그를 설명하자면 첫 번째 줄에 Exception in thread "main" 부분은 멀티 스레드 환경에서 어떤 스레드에서 오류가 발생하였는지 스레드의 이름을 알려줍니다. 

이 로그에서는 "main"입니다.
그 뒤에 어떤 Exception 클래스가 발생하였는지 확인합니다. 

이 로그의 경우는 "java.lang.IndexOutOfBoundsException" 예외가 발생하였습니다.

그 후에 상세한 정보가 표시되기도 하며 예외에 따라 null 이 출력 되는 경우도 있습니다.

"Index: 6, Size: 6" 내용을 대략 유추하면 총 크기가 6인 배열에서는 0-5까지 호출 가능하지만 6을 호출하여 오류가 발생하였다고 유추할 수 있습니다.

첫 줄에서 오류의 대략적인 원인과 종류를 알아봤습니다.
그 이후의 StackFlow에서 분석해야 할 내용은 내 소스의 범위에서 최상단 StackFlow을 찾아야 합니다.

2-3줄은 java.util.ArrayList 라이브러리에서 에러가 발생한 것을 확인할 수 있습니다.

at java.util.ArrayList.rangeCheck(ArrayList.java:635) 
at java.util.ArrayList.get(ArrayList.java:411)

또한 그 입구가 ArrayList.get 함수를 호출하여 발생한 오류임을 짐작 할 수 있습니다.

4번째 줄부터 내가 코딩한 클래스라면 해당 위치의 클래스와 함수를 라인을 유심히 봐야 합니다.

at ExceptionTest.Exception04_IndexOutOfBoundException(ExceptionTest.java:110)
여기서 작업 중인 ExceptionTest 클래스에서 오류의 원인을 제공했을 가능성이 높다는 것을 유추할 수 있습니다.

Exception04_IndexOutOfBoundException에서 오류를 유발하였으며, ExceptionTest.Java 파일의 110번 줄이라는 정보를 제공합니다.

Eclipse 등의 환경에서는 해당 ExceptionTest.Java:110을 마우스 왼쪽 클릭할 때 해당 위치로 이동하게 됩니다.

해당 위치부터 값을 확인하며 한 단계씩 내려가며 확인한다면 오류를 비교적 쉽게 해결할 수 있습니다.

NullPointerException

1) 서론
가장 많이 발생되는 Exception으로 java.lang.NullPointerException입니다.
이 예외는 Pointer 개념Null을 이해해야 쉽게 클리어할 수 있습니다.
Java는 객체 지향 언어이며 참조 타입은 Instance로 생성(new) 할 때 주소가 할당됩니다.
2) Null
Null은 사전적 의미로  "무엇의 뜻, 가치, 효력 따위가 없음"이며, Java에서 객체가 초기화되지 않은 상태 값을 "null"로 표기하며 코드상으로 보면 아래 코드와 같습니다.
Byte b = null; 
Byte c;
b와 c는 동일한 null 값을 가지고 프로그램이 초기화됩니다.
3) 자료형
자료형에는 Primitive Type (기본형) 과 Reference Type (참조형)이 존재합니다.

기본형은 객체에 자료가 담겨 있는 형태이며 참조형은 객체에 자료가 담겨있는 주소를 담고 있습니다.
[종류]

기본형

참조형 

 byte

Byte 

 char

Character 

 short 

Short

 int 

Integer 

 long 

Long 

 float 

Float 

 double 

Double 

 String
 Object


4) Reference Type의 객체를 참조할 때 오류가 발생
아래와 같은 public 객체와 같은 경우는 Class 외부에서 값을 할당할 수 있으므로 컴파일에서 못 잡아내고 런타임에 오류가 발생합니다.
[JAVA]
    public Byte ref_b; 
     
    public void Exception01_NullPointException() 
        // ref_b의 주소값이 출력됨 
        System.out.println(ref_b);             
        // ref_b값이 null인 상태에서 클래스로 인식하여 
        // toString()을 호출하는 순간 오류 발생. 
        System.out.println(ref_b.toString());  
    }
[Console]
null 
Exception in thread "main" java.lang.NullPointerException 
at ExceptionTest.Exception01_NullPointException(ExceptionTest.java:19) 
at ExceptionTest.main(ExceptionTest.java:11)

이를 해결하기 위해서는 ref_b에 적절한 초기값을 배치하거나 null 검사를 통해 null 일 경우 다른 값으로 대체 출력하는 방법이 있습니다.

    public Byte        ref_b; 
     
    public void Exception01_NullPointException() 
    { 
        //ref_b를 null인지 검사하여 null일 경우 "0"으로 대체하여 출력한다. 
        System.out.println(ref_b == null ? "0" : ref_b.toString()); 
        //Wrapper Class는 new는 사용하지 않아도 컴파일러에서 자동으로 new해준다. 
        ref_b = 10; 
        //일반 클래스는 아래와 같이 Constructor(생성자)를 통해 객체를 초기화 한다. 
        ref_b = new Byte((byte)10); 
        System.out.println(ref_b); 
    }
또한 null이 예상되는 Parameter의 경우 아래와 같은 경우에도 오류가 발생합니다.
이유는 래퍼 클래스 b의 참조 주소는 null이며 10과 비교하기 위해서 래퍼 클래스의 b.intValue() 함수를 자동으로 실행하여 결과 값을 얻어오고 비교합니다.
    public void Exception01_NullPointException(Byte b)  
    {  
        //이 비교는 b.intValue() < 10과 같이 동작된다. 
        if(b < 10) System.out.println("Byte는 10보다 작습니다."); 
        else       System.out.println("Byte는 10보다 큽니다.");  
    }
따라서 이러한 경우에는 null의 선 처리가 필요합니다.
    public void Exception01_NullPointException(Byte b) 
    { 
        if(b != null) 
        { 
            if(b < 10) System.out.println("Byte는 10보다 작습니다."); 
            else       System.out.println("Byte는 10보다 큽니다."); 
        } 
    }