개발 공부 기록
[JAVA] 예외 처리에 대해서 본문
본 포스팅은 [이것이 자바다]를 참고하여 작성하였습니다.
.
.
.
☑️ 예외란?
예외는 사용자의 잘못된 조작 혹은 개발자의 잘못된 코딩으로 발생하는 프로그램 오류를 말합니다. 우리는 예외가 발생하면 예외 처리를 통해 프로그램을 곧바로 종료시키지 않고 정상적으로 실행되도록 할 수 있습니다.
예외에는 Exception(일반 예외)과 RuntimeException(실행 예외)이 있습니다. Exception은 컴파일러 체크 예외로 불리기도 하며, 예외 처리 코드가 없다면 컴파일 오류가 발생하게 됩니다. 반면 RuntimeException은 컴파일 과정에서 예외 처리 코드를 검사하지 않습니다. 이 둘은 컴파일 시 예외 처리를 확인하는데 있어선 차이가 있지만, 결국 둘 모두 예외 처리가 필요합니다.
JVM은 프로그램 실행 도중 예외가 발생하면 java.lang.Exception 클래스를 상속받는 헤당 예외 클래스로 객체를 생성합니다. 그러면 우리는 예외 처리 코드에서 이 예외 객체를 이용할 수 있습니다.
☑️ RuntimeException (실행 예외)
RuntimeException은 컴파일러가 확인을 하지 않기 때문에 오직 개발자의 경험에 의해 예외 처리 코드를 삽입해야 합니다. 만일 적절한 예외 처리 코드를 작성하지 않는다면, 해당 예외 발생시 프로그램은 곧바로 종료됩니다. 아래는 자바 프로그램에서 자주 발생되는 실행 종류입니다.
1. NullPointerException
객체 참조가 없는 상태로, null 값을 갖는 참조 변수에게 객체 접근 연산자인 도트(.)를 사용했을 때 발생합니다.
public class ExceptionExample {
public static void main(String[] args) {
String data = null; //data는 null값을 가지고 있으므로 String 객체를 참조하고 있지 않음.
System.out.println(data.toString()); //String 객체의 toString() 메소드 호출 -> NullPointerException 발생
}
}
2. ArrayIndexOutOfBoundsException
배열에서 인덱스 범위를 초과하여 사용할 경우 발생합니다.
public class ExceptionExample {
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);
}
}
3. NumberFormatException
Integer.parseInt()나 Double.parseDouble() 등을 통해 문자열을 숫자로 변환할 때, 매개값인 문자열이 숫자로 변환될 수 없다면 발생합니다.
public class ExceptionExample {
public static void main(String[] args) {
String data1 = "a100";
int value = Integer.parseInt(data1); //NumberFormatException 발생
System.out.println(data1);
}
}
4. ClassCastException
타입 변환(Casting)은 상위 클래스와 하위 클래스 혹은 구현 클래스와 인터페이스 간에 발생합니다. 이러한 관계가 아니라면 클래스는 다른 클래스로 Casting할 수 없습니다. 억지로 Casting할 경우 ClassCastException이 발생합니다.
추상 클래스 Animal이 있고 하위 클래스인 Dog와 Cat이 있다고 가정해봅시다.
Animal animal = new Dog();
Cat cat = (Cat) animal;
Animal 타입 변수에 대입된 Dog 객체가 Cat 타입 변수로 변환을 하면 안됩니다. 대입된 객체인 Dog로만 변환이 가능합니다.
ClassCastException을 발생시키지 않으려면 타입 변환이 가능한지 미리 instanceof 연산자로 확인하는 것이 좋습니다. 결과가 true라면 좌항 객체를 우항 타입으로 변환이 가능하다는 의미입니다.
public class ExceptionExample {
public static void main(String[] args) {
Dog dog = new Dog();
changeDog(dog);
}
public static void changeDog(Animal animal) {
if(animal instanceof Dog) {
Dog dog = (Dog) animal;
}
}
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
}
☑️ 예외 처리 코드 형식
예외가 발생했을 때 프로그램이 종료되지 않도록 예외 처리 코드를 작성해줍니다. 자바 컴파일러는 Exception(일반 예외)이 일어나면 개발자에게 강제적으로 예외 처리 코드를 작성하도록 요구하지만, RuntimeException은 컴파일러가 확인해주지 않가 때문에 개발자의 역량으로 작성해야 합니다.
예외 처리 코드는 try-catch-finally 블록을 이용하여 작성합니다.
try {
//예외 발생 가능 코드
//try 블록이 정상적으로 실행되면 catch는 실행되지 않고 finally 블록으로 넘어감.
//try 블록에서 예외 발생할 경우 즉시 실행을 멈추고 catch 블록으로 넘어감.
} catch(예외클래스 e) {
//예외 처리
} finally {
//항상 실행되는 블록 (예외 발생 여부와 관계없이 항상 실행돼야 할 내용이 있을 경우에만 작성)
//생략 가능
}
☑️ 예외 종류에 따른 처리 코드
1. 다중 catch
try 블록 내부에서 발생할 수 있는 다양한 종류의 예외를 처리하기 위해 다중 catch 블록을 작성할 수 있습니다. catch 블록이 여러개여도 try 블록에서는 하나의 예외만 발생해도 즉시 실행을 멈춰 적절한 catch 블록으로 이동하기 때문에 결국 단 하나의 catch 블록만 실행됩니다.
try {
//ArrayIndexOutOfBoundsException 발생 코드
//NumberFormatException 발생 코드
} catch(ArrayIndexOutOfBoundsException e) {
//예외 처리1
} catch(NumberFormatException e) {
//예외 처리2
}
2. catch 순서
다중 catch 블록 작성시 주의해야 할 점은, 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 합니다. try 블록에서 예외가 발생하면 catch 블록은 위에서부터 아래로 탐색되는데, 이때 상위 예외 클래스가 먼저 탐색된다면 하위 예외 클래스는 실행되지 않습니다.
try {
//ArrayIndexOutOfBoundsException 발생
//NumberFormatException 발생
} catch(Exception e) {
//예외 처리1 (두 예외 여기서 처리됨.)
} catch(ArrayIndexOutOfBoundsException e) {
//예외 처리2 (실행되지 않음.)
}
3. 멀티 catch
하나의 catch 블록에서 여러 개의 예외를 처리할 수 있는 기능으로, catch 괄호 () 안에 동일하게 처리하고 싶은 예외를 |로 연결해주면 됩니다.
try {
//ArrayIndexOutOfBoundsException 또는 NumberFormatException 발생
//다른 Exception 발생
} catch(ArrayIndexOutOfBoundsException | NumberFormatException e) {
//예외 처리1
//ArrayIndexOutOfBoundsException과 NumberFormatException 모두 여기서 처리됨.
} catch(Exception e) {
//예외 처리2
}
☑️ 예외 떠넘기기
경우에 따라서는 throws 키워드를 통해 메소드를 호출한 곳으로 예외를 떠넘길 수 있습니다. throws는 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 합니다. throws 뒤에는 떠넘길 예외 클래스를 쉼표로 구분하여 나열해주면 됩니다.
리턴타입 메소드명(매개변수, ..) throws 예외클래스1, 예외클래스2, .. {
}
throws가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야 하며, catch 블록에서 떠넘겨 받은 예외를 처리합니다.
public static void main(String[] args) {
try {
findClass();
} catch (ClassNotFoundException e) {
System.out.println("클래스가 존재하지 않습니다.");
}
}
public static void findClass() throws ClassNotFoundException{
Class clazz = Class.forName("java.lang.String2");
}
main()에서 Class.forName()을 사용하는 findClass() 메소드가 예외를 떠넘겼습니다. catch 블록은 findClass() 선언부 끝에 작성된 throws ClassNotFoundException에서 예외를 처리하도록 합니다. 아래와 같이 main()에서 throws를 사용해 예외를 떠넘길 수 있지만, 이것은 좋지 않은 방법입니다.
public static void main(String[] args) throws ClassNotFoundException {
findClass();
}
'JAVA > Concept' 카테고리의 다른 글
[JAVA] 비트마스크(Bit Mask)란? (0) | 2023.09.20 |
---|---|
[JAVA] Maven과 Gradle이란? - 개념과 차이점 (0) | 2023.09.08 |
[JAVA] 인접 행렬과 인접 리스트 (0) | 2023.08.09 |
[Java] BufferedReader, StringTokenizer 클래스 사용방법 (0) | 2023.08.04 |