오류(Error)가 프로그램 자체 또는 JVM 등의 원인으로 발생하는 문제를 말한다면, 예외(Exception)는 사용자의 조작이나 개발자의 코딩 실수로 발생된 문제를 말한다. 오류는 개발자가 고칠 수 없지만 예외는 대처가 가능하기 때문에, 코드를 작성할 때부터 예외 처리를 미리 해 놓는다. 오류나 예외가 발생한 경우 프로그램은 강제로 종료되는데, 예외 처리를 하면 정상적으로 프로그램 종료를 할 수 있다.
예외는 컴파일 시점에 체크 가능한 Compile Checked Exception, 실행을 해야 체크 가능한 Compile Unchecked Exception으로 분류된다. 전자는 처리가 되어야만 코드 실행이 가능하기 때문에 예외 처리에 강제성을 띤다. 여기서 처리를 하지 않으면 컴파일 에러가 발생한다. 그 예로는 IOException, SQLException, ClassNotFoundException 등이 있다. 그리고 후자는 실행 전까지 예외 발생을 알 수 없으며, 처리가 안 되면 실행 후 런타임 에러가 뜬다. 대표적으로 ArithmeticException, NullPointerException, ClassCastException, ArrayIndexOutOfBoundsException 등의 예외가 있다.
try ~ catch문
예외를 처리할 때는 try ~ catch문을 사용한다. try 블록 내에 예외 발생 가능성이 있는 코드를 작성하고, 만약 여기서 예외가 발생하면 예외 객체를 만들어 catch문으로 던진다. catch문에는 예외 처리 코드를 적어놓고, 예외가 발생하면 해당 코드를 실행한 후 프로그램을 정상 종료시킨다. 아래 코드는 0으로 나눴을 때 발생하는 ArithmeticExcpetion과 null값을 대상으로 했을 때 발생하는 NullPointerException의 예시다.
int a = 3, b = 0;
try {
System.out.println("try start"); //출력
System.out.println(a/b); //예외 발생
System.out.println("try end");
} catch (ArithmeticException e) {
e.getStackTrace();
System.out.println("ArithmeticException 발생"); //출력
}
String str = null;
try {
System.out.println(str.length()); //예외 발생
} catch (NullPointerException e) {
e.getStackTrace();
System.out.println("NullPointerException 발생"); //출력
}
또한 하나의 try문 안에서 복수개의 예외를 다룰 수도 있다. 그럴 경우 catch문을 예외 별로 하나씩 만들거나 한 번에 처리하는 방법이 있다. 예외 클래스도 상속 형태로 되어있기 때문에, 최상위인 Exception 클래스를 상속받는 다양한 예외 클래스가 존재한다. 그래서 catch문에 Exception 객체로 받게 되면 모든 예외가 처리되는 대신, 예외의 종류는 알 수 없게 된다. 만약 예외 별 catch문을 따로 만들 때는, 하위 타입 예외를 먼저 기술해야 모든 catch문이 실행된다.
int a = 3, b = 0;
try {
System.out.println(a/b); //예외 발생
} catch (Exception e) { //최상위 예외 클래스
e.printStackTrace();
}
int a = 3, b = 0;
String str = null;
try {
System.out.println(a/b); //예외 발생
System.out.println(str.length()); //실행 안되고 catch문으로 넘어감
} catch (ArithmeticException e) {
System.out.println("a/b 불가능");
} catch (NullPointerException e) {
System.out.println("str.length() 불가능");
}
위의 코드들에서 catch문에 printStackTrace() 메서드는 예외 코드의 호출 순서와 예외 발생 상황을 출력하는 메서드다. 이 외에도 원인 메시지를 출력하는 getMessage(), 오버라이딩된 메시지를 출력하는 getLocalizedMessage()가 있다. 오버라이딩을 하지 않으면 getMessage()와 똑같다.
finally문
예외 처리 시 catch문 아래에 작성할 수 있는 finally문이 있는데, finally문 내의 코드는 예외가 발생하든 하지 않든, try문 안에 return문이 있어서 코드가 종료되든 아니든 무조건적으로 실행된다. 주로 JDBC 등 DB를 연결하는 작업에서 예외가 발생하더라도 자원은 반환하기 위해 사용된다. 아래는 try ~ catch ~ finally문을 쓴 예시다.
try {
try {
String str = null;
System.out.println(str.length()); //NullPointerException 발생
System.out.println("출력문1");
} catch (ArithmeticException e) {
System.out.println("출력문2");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("출력문3");
}
System.out.println("출력문4");
} catch (ClassCastException e) {
System.out.println("출력문5");
} catch (NullPointerException e) {
System.out.println("출력문6"); //출력
} finally {
System.out.println("출력문7"); //출력
}
예외 처리 위임(throws)
예외 발생 가능성이 있는 코드를 작성하면서 try ~ catch문을 쓰지 않고 처리를 위임하는 방법이 있다. 예외 코드가 적혀있는 메서드 헤더에 'throws 예외명' 을 붙이면, 위임된 예외는 해당 메서드를 호출하는 곳에서 처리하게 된다.
public static void main(String[] args) { //위임받음
try {
대리();
} catch (Exception e) { //throws 한 예외 하위클래스는 작성 불가능
System.out.println("throws"); //출력
}
}
public static void 대리() throws ArithmeticException, NullPointerException { //위임받고 던짐
사원();
}
public static void 사원() throws ArithmeticException { //예외 던짐
System.out.println(3/0);
}
사용자 정의 예외(throw)
사용자가 직접 Exception 클래스를 상속시켜 예외 클래스를 정의할 수도 있다. 예를 들어서 시험 점수를 입력할 경우 0에서 100 사이의 숫자가 아니면 예외를 발생시킬 수 있다는 뜻이다. 이렇게 직접 정의하는 예외는 알아보기 쉽다는 장점도 있다. 아래 코드처럼 예외가 발생할 곳에 throw로 예외 상황이나 처리 결과를 Exception 클래스로 전달하면 기존 예외 클래스처럼 사용이 가능해진다.
class InvalidScoreInputException extends Exception { //사용자 정의 예외클래스
public InvalidScoreInputException(String s) {
super(s); //Exception 클래스로 오류 메시지 전달
}
}
public class Ex {
public static void main(String[] args) {
int score = 1100;
try {
grade(score);
} catch (Exception e) {
System.out.println(e.getMessage()); //점수 입력 오류
}
try {
grade2(score);
} catch (InvalidScoreInputException e) {
System.out.println(e.getMessage()); //사용자 정의 예외 : 점수 입력 오류
}
}
public static void grade(int score) throws ArithmeticException { //기존 예외
if(score<0 || score>100) {
throw new ArithmeticException("점수 입력 오류");
}
}
public static void grade2(int score) throws InvalidScoreInputException { //사용자 정의 예외
if(score<0 || score>100) {
throw new InvalidScoreInputException("사용자 정의 예외 : 점수 입력 오류");
}
}
}
'Study > Java' 카테고리의 다른 글
[Java]인터페이스(Interface)와 중첩 클래스 (0) | 2022.02.26 |
---|---|
[Java]추상화(abstract)와 static, 싱글톤 (0) | 2022.02.23 |
[Java]상속과 참조 형 변환, 다형성 (0) | 2022.02.07 |
[Java]생성자와 패키지, 접근제한자 (0) | 2022.01.26 |
[Java]OOP - 클래스와 메서드 (0) | 2022.01.23 |