본문 바로가기

Java 기본 문법 - 참조 서적 [이것이 자바다 - 한빛미디어]/8. 예외 처리

1. Java 자바 [예외 처리] - 예외와 예외 클래스

에러(Error)

 

컴퓨터의 하드웨어 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것.

에러는 JVM 실행에 문제가 발생했다는 의미이므로 JVM 위에서 실행되는 프로그램도 실행 불능이 된다.

 

예외(Exception)

 

사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류.

예외가 발생하면 프로그램은 곧바로 종료된다. (에러와 동일)

 

 

예외의 종류

 

예외에는 2 가지 종류가 있다.

 

- 일반 예외(Exception)

- 실행 예외(RuntimeException)

 

 

- 일반 예외 (컴파일러 체크 예외)

 

자바 소스를 컴파일하는 과정에서 예외 처리 코드가 필요한 지 검사한다.

예외 처리 코드가 없다면 컴파일 오류가 발생한다.

 

 

- 실행 예외

 

컴파일 과정에서 예외 처리 코드를 검사하지 않는 예외

 

두 가지 예외는 컴파일 시에 예외 처리를 확인하는 차이일 뿐, 모두 예외 처리가 필요하다.

 

자바는 예외를 클래스로 관리한다.

JVM은 프로그램을 실행하는 도중 예외가 발생하면, 해당 예외 클래스로 객체를 생성한다.

이후, 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다.

 

모든 예외 클래스들은 그림과 같이 java.lang.Exception 클래스를 상속받는다.

 

모든 예외 클래스는 java.lang.Exception 클래스를 상속한다.

 

일반 예외 클래스와 실행 예외 클래스를 구별하는 방법

 

일반 예외는 Exception 을 상속받지만, RuntimeException 을 상속받지 않는 클래스이고,

실행 예외는 RuntimeException을 상속받은 클래스들이다.

 

RuntimeException 역시 Exception을 상속받지만, JVM은 RuntimeException을 상속했는지 여부를 보고

실행 예외를 판단한다.

 

java.lang.RuntimeException 을 상속하는 실행 예외들

 

실행 예외 (RuntimeException)

 

실행 예외는 자바 컴파일러가 체크하지 않기 때문에 개발자에 의해 예외 처리 코드를 삽입해야 한다.

만약 예외 처리 코드를 넣지 않은 경우, 해당 예외가 발생하면 프로그램은 곧바로 종료된다.

 

- NullPointException

(java. lang.NullPointerException)

 

가장 빈번하게 발생하는 실행 예외이다.

객체 참조가 없는 상태, 즉 null 값을 갖는 참조 변수로 객체 접근 연산자 도트( . ) 를 사용했을 때 발생한다.

 

객체가 없는 상태에서 객체를 사용할 때 발생하게 된다.

 

NullPointerExceptionExam.java : NullPointerException 이 발생하는 경우

 

public class NullPointerExceptionExam {
    public static void main(String[] args) {
    	String data = null;
        System.out.println(data.toString());   // 예외 발생!
    }
}

 

data 변수는 null 값을 가지기 때문에 String 객체를 참조하고 있지 않다.

data.toString() 에서 String 객체의 toString() 메소드를 호출 시 NullPointerException 이 발생한다.

 

 

- ArrayIndexOutOfBoundsException

 

배열에서 인덱스 범위를 초과해서 사용할 경우 발생한다.

 

예를 들어 길이가 3인 int[] = new int[3]; 배열을 선언했을 때,

배열 항목을 지정하기 위해 arr[0] ~ arr[2] 까지 사용할 수 있다.

하지만 arr[3]을 사용 시 인덱스 범위를 초과하기 때문에 예외가 발생한다.

 

ArrayIndexOutBoundsExceptionExam.java

 

public class ArrayIndexOutOfBoundsExceptionExam {
    public static void main(String[] args) {
    	String data1 = args[0];   // 예외 발생!
        String data2 = args[1];
        
        System.out.println("args[0] : "+ data1);
        System.out.println("args[1] : "+ data2);
    }
}        

 

실행 매개값 args 배열을 매개 값으로 넘겨주지 않았기 때문에 args[0], args[1] 과 같이 인덱스를 사용할 수 없다.

 

* 이클립스 메뉴 [Run → Run Configurations ... ] 에서 [Arguments] 탭의 [Program arguments] 입력란에

두 개의 매개 값을 입력하면 예외가 발생하지 않는다.

 

따라서 배열 값을 읽기 전에 배열의 길이를 먼저 조사해서 예외 발생을 사전에 방지해야 한다.

실행 매개값이 없거나 부족할 경우, 조건문을 이용해서 사용자에게 실행 방법을 알린다.

 

ex) ArrayIndexOutOfBoundsExceptionExam.java

 

public class ArrayIndexOutOfBoundsExceptionExam {
    public static void main(String[] args) {
    	if(args.length == 2) {
        	String data1 = args[0];
            String data2 = args[1];
            System.out.println("args[0] : "+ data1);
            System.out.println("args[1] : "+ data2);
        } else {
        	System.out.println("[실행 방법]");
            System.out.println("java ArrayIndexOutOfBoundsExceptionExam");
            System.out.println("값1    값2");
        }
    }
}

 

 

- NumberFormatException

 

문자열로 되어있는 데이터를 숫자로 변경하는 경우가 자주 발생한다.

문자열을 숫자로 변환하는 방법은 여러 가지가 있지만, 가장 많이 사용되는 메소드는 아래와 같다.

 

Integer.parseInt(String s);

 

반환 타입 : int

 

주어진 문자열을 정수로 변환해서 반환한다.

 

Double.parseDouble(String s);

 

반환 타입 : double

 

주어진 문자열을 실수로 변환해서 반환한다.

 

Integer, Double 은 포장(Wrapper) 클래스라고 한다.

이 클래스의 정적 메소드인 parseXXX() 메소드를 이용하면 문자열을 숫자로 변환할 수 있다.

이 메소드들은 매개 값인 문자열이 숫자로 변환될 수 있다면 숫자를 리턴하지만,

 

숫자로 변환될 수 없는 문자가 포함되어 있다면 java.lang.NumberFormatException 예외가 발생한다.

 

NumberFormatExceptionExam.java

 

public class NumberFormatExceptionExam {
    public static void main(String[] args) {
    	String data1 = "100";
        String data2 = "a100";
        
        int value1 = Integer.parseInt(data1);
        int value2 = Integer.parseInt(data2);   // NumberFormatException 발생
        
        int result = value1 + value2;
        System.out.println(data1 + "+" + data2 + "=" + result);
    }
}

 

- ClassCastException

 

타입 변환(Casting)은 상위 클래스와 하위 클래스 간에 발생하고,

구현 클래스와 인터페이스 사이에도 발생한다.

 

이러한 관계가 아니면 클래스는 다른 클래스로 타입 변환이 불가능하다.

 

따라서 억지로 타입 변환을 시도하면 ClassCastException 예외가 발생한다.

 

예를 들어 아래와 같이 상속 관계와 구현 관계가 있을 때

 

상속 관계와 구현 관계

 

올바른 타입 변환 코드는 아래와 같다.

 


Animal animal = new Dog();

Dog dog = (Dog) animal;

RemoteControl rc = new Television();

Television tv = (Television) rc;

 

Animal 타입 변수 animal 에 대입된 객체가 Dog 이므로 다시 Dog 타입으로 변환하는 것은 문제되지 않는다.

마찬가지로 RemoteControl 타입 변수 rc 에 대입된 객체가 Television 이므로

다시 Television 타입으로 변환하는 것은 아무 문제가 되지 않는다.

 

하지만 아래와 같이 타입 변환을 하면 ClassException 이 발생한다.

 


Animal animal = new Dog();

Cat cat = (Cat) animal;  // 예외 발생


RemoteControl 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;

}


RemoteControl rc = new Audio();

if(rc instanceof Television) {
    Television tv = (Television) rc;
} else if(rc instanceof Audio) {
    Audio audio = (Audio) rc);
}

 

ClassCastExceptionExam.java

 

public class ClassCastExceptionExample {
    public static void main(String[] args) {
    	Dog dog = new Dog();
        changeDog(dog);
        
        Cat cat = new Cat();
        changeDog(cat);      // Cat 객체를 매개값으로 주었기 때문에 Dog 타입으로 변환 불가!!
    }
    
    public static void changeDog(Animal animal) {
    	// if(animal instanceof Dog) {
        	Dog dog = (Dog) animal;        // ClassCastException 발생 가능!
        //}
    }
}

class Animal {  }
class Dog extends Animal {  }
class Cat extends Animal {  }