innn

예외 클래스 본문

JAVA/자바

예외 클래스

33삼 2022. 9. 26. 14:57

자바에서는 컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것을 에러(error)라고 한다. 그리고 에러 이외에 프로그램 자체에서 발생하는 오류는 예외(exception)라고 부른다. 

예외의 종류로 어떤 것이 있고, 언제 발생하는지 알아보자.

 

예외exeption란 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류를 말한다. 예외가 발생되면 프로그램은 곧바로 종료된다는 점에서는 에러와 비슷하다. 그러나 예외는 예외 처리exception handling를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있다.

 

자바는 예외가 발생할 가능성이 높은 코드를 컴파일할 때 예외 처리 유무를 확인한다. 만약 예외 처리 코드가 없다면 컴파일이 되지 않는다. 하지만 모든 예외에 대해서 예외 처리 유무를 확인하는 것은 아니다 이것을 이해하려면 예외의 종류부터 알아야한다. 

 

예외와 예외 클래스

예외는 두 가지 종류가 있다. 하나는 일반 예외exception이고, 다른 하나는 실행 예외runtime exception이다. 

 

일반 예외는 컴파일러 체크 예외라고도 하는데, 프로그램 실행시 예외가 발생할 가능성이 높기 때문에 자바 소스를 컴파일하는 과정에서 해당 예외 처리 코드가 있는지 검사한다. 만약 예외 처리 코드가 없다면 컴파일 오류가 발생한다. 

 

실행 예외는 컴파일러 넌 체크 예외라고도 하는데, 실행 시 예측할 수 없이 갑자기 발생하기 때문에 컴파일하는 과정에서 예외 처리 코드가 있는지 검사하지 않는다. 

 

자바에서는 예외를 클래스로 관리한다. JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성한다. 그리고 나서 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다. 모든 예외 클래스는 다음과 같이 java.lang.Exception 클래스를 상속 받는다. 

일반예외

일반 예외와 실행 예외의 클래스는 RuntimeException 클래스를 기준으로 구별한다. RuntimeException의 하위 클래스가 아니면 일반 예외 클래스이고, 하위 클래스이면 실행 예외 클래스이다. 클래스 상속 관계에서 부모(조상)에 RuntimeException이 있다면 실행 예외 클래스이다.

RuntimeException의 하위 클래스인 실행 예외

실행 예외

실행 예외는 자바 컴파일러가 체크하지 않기 때문에 오로지 개발자의 경험에 의해서 예외 처리 코드를 작성해야 한다. 만약 개발자가 실행 예외에 대해 예외 처리 코드를 넣지 않았을 경우, 해당 예외가 발생하면 프로그램은 곧바로 종료된다. 

 

> 자바 프로그램 개발 경력이 풍부하다면 언제, 어떤 실행 예외가 발생하는지 쉽게 알 수 있지만, 이제 시작하는 개발자라면 지금부터 설명하는 실행 예외를 잘 익혀두자. 자바 프로그램에서 자주 발생하는 실행 예외이므로 언제 발생되고, 어떤 오류 메시지가 출력되는지 잘 알아둘 필요가 있다. 

 

NullPointerException

자바 프로그램에서 가장 빈번하게 발생하는 실행 예외는 java.lang.NullPointerException이다. 이것은 객체 참조가 없는 상태, 즉 null 값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생한다. 객체가 없는 상태에서 객체를 사용하려 했으니 예외가 발생하는 것이다. 

 

 

5라인에서 data 변수는 null 값을 가지고 있기 때문에 String 객체를 참조하고 있지 않다. 하지만 6라인에서 String 객체의 toString() 메소드를 호출하고 있다. 여기서 NullPointerException이 발생한다. 

 

실행 결과

프로그램에서 예외가 발생하면 예외 메시지가 Console 뷰에 출력되면서 프로그램이 종료된다. Console 뷰에 출력되는 내용에는 어떤 예외가 어떤 소스의 몇 번째 코드에서 발생했는지에 대한 정보가 들어있다. 앞 예제의 경우 NullPointerExceptionExample.java 소스의 6번째 코드에서 발생했음을 알 수 있다. Console 뷰에서 밑줄 처리된 NullPointerExceptionExample.java:6을 마우스로 클릭하면 소스 편집기는 정확히 NullPointerExceptionExample.java의 6라인을 하이라이팅해준다. 

 

이렇게 하이라이팅 해준다!

 

결론 : NullPointerException = null 값을 갖는 참조변수에 접근할 때 발생

 

ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과할 경우 실행 예외인 java.lang.ArrayIndexOutOfBoundsException이 발생한다. 예를 들어 길이가 3인 int [] arr = new int[3] 배열을 선언했다면, 배열 항목을 지정하기 위해 arr[0] ~ arr[2]를 사용할 수 있다. 하지만 arr[3]을 사용하면 인덱스 범위를 초과했기 때문에 ArrayIndexOutOfBoundsException이 발생한다.

 

 

위 예제를 실행하면 5라인에서 ArrayIndexOutOfBoundsException이 발생한다. 그 이유는 2개의 실행 매개값을 주지 않았기 때문에 args[0], args[1] 과 같이 인덱스를 사용할 수 없기 때문이다.

 

결과 콘솔창 어레이 인덱스 범위 초과의 에러가 떴다.

이클립스에서 [Run] - [Run Configuration] 메뉴를 선택한 후, [Arguments] 탭의 [Program arguments] 입력란에 다음과 같이 2개의 매개값을 입력하고 실행하면 예외가 발생하지 않는다. 

 

또는 아래와 같이 수정하면 ArrayIndexOutOfBoundsException이 발생하지 않는 좋은 프로그램이 된다. 배열값을 읽기 전에 배열의 길이를 먼저 조사하는 것이다. 실행 매개값이 없거나 부족할 경우 조건문을 이용해서 사용자에게 알려준다. 

 

 

결론 : ArrayIndexOutOfBoundsException = 실행 매개값이 없거나 부족한 경우에 발생

 

NumberFormatException

프로그램을 개발하다 보면 문자열로 되어 있는 데이터를 숫자로 변경하는 경우가 자주 발생한다. 문자열을 숫자로 변환하는 방법은 여러 가지가 있지만 주로 다음 코드를 가장 많이 사용한다.

Integer와 Double은 포장Wrapper 클래스이다. 이 클래스의 정적 메소드인 parseXXX() 메소드를 이용하면 문자열을 숫자로 변환할 수 있다. 이 메소드들을 매개값인 문자열이 숫자로 변환될 수 있다면 숫자를 리턴하지만, 숫자로 변환될 수 없는 문자가 포함되어 있다면 java.lang.NumberFormatException을 발생시킨다.

 

 

data1 변수의 "100" 문자열은 숫자로 변환이 가능하기 때문에 8라인이 정상적으로 실행된다. 

하지만, data2 변수의 "a100" 문자열은 숫자로 변환할 수 없기 때문에 9라인에서 다음과 같이 NumberFormatException이 발생한다. 

 

실행결과 콘솔창

 

결론 : NumberFormatException = 문자열을 숫자로 변환될 수 없을 때 발생

ClassCastException

타입 변환Casting은 상위 클래스와 하위 클래스 간에 발생하고 구현 클래스와 인터페이스 간에도 발생한다. 이러한 관계가 아니라면 클래스는 다른 타입으로 변환할 수 없기 때문에 ClassCastException이 발생한다. 예를 들어 다음과 같이 상속 관계와 구현 관계가 있다고 가정해보자.

 

다음은 올바른 타입 변환을 보여준다. Animal 타입 변수에 대입된 객체가 Dog이므로 다시 Dog 타입으로 변환하는 것은 아무런 문제가 없다. 마찬가지로 RemoteControl 타입 변수에 대입된 객체가 Television 이므로 다시 Television 타입으로 변환하는 것도 아무런 문제가 없다. 

Animal animal = new Dog();

Dog dog = (Dog) animal;
RemoteControle rc = new Television();

Television tv = (Television) rc;

그러나 아래와 같이 타입 변환을 하면 ClassCastException이 발생한다. 대입된 객체가 아닌 다른 클래스 타입으로 타입변환을 했기 때문이다.

Animal animal = new Dog();

Cat cat = (Cat) animal;
RemoteControle rc = new Television();

Audio audio = (Audio) rc;

 

ClassCastException을 발생시키지 않으려면 타입 변환 전에 변환이 가능한지 instanceof 연산자로 확인하는 것이 좋다. instanceof 연산의 결과가 true이면 좌항 객체를 우항 타입으로 변환이 가능하다는 뜻이다. 

Animal animal = new Dog();

if(animal instanceof Dog) {

Dog dog = (Dog) animal;

} else if(animal instanceof Cat) {

Cat cat = (Cat) animal;

}
RemoteControle rc = new Audio();

if(rc instanceof Television) {

Television tv = (Television) rc;

} else if(rc instanceof Audio) {

Audio audio = (Audio) rc;

}

 

 

예제를 실행하면 14라인에서 ClassCastException이 발생한다. 그 이유는 9라인에서 Cat 객체를 매개값으로 주어서 Dog 타입으로 변환할 수 없기 때문이다.

이렇게 잘못된 매개값이 들어올 수 있기 때문에 changeDog() 메소드에서 13라인과 15라인의 주석을 풀어 타입 체크를 먼저 하는 것이 좋다. 

'JAVA > 자바' 카테고리의 다른 글

API 도큐먼트 읽는 방법  (0) 2022.09.27
예외 처리  (0) 2022.09.26
UML 클래스 다이어그램의 관계  (0) 2022.09.06
starUML을 이용해 UML 그리기  (0) 2022.09.02
2022.07.18 자바 웹 프로그래밍과 ERD  (0) 2022.07.18