오류(error)는 컴파일 오류와 실행 오류로 나뉜다.
컴파일 오류(compile error): 프로그램 코드 작성 중 발생하는 문법적 오류
실행 오류(runtime error): 실행 중인 프로그램이 의도하지 않은 동작을 하거나(bug) 프로그램이 중지되는 오류
실행 오류 발생 시 비정상 종료는 서비스 운영에 치명적이므로, 오류가 발생할 수 있는 경우에 로그(log, 기록)를 남겨 추후 이를 분석하여 원인을 찾아야 한다. 자바는 예외 처리를 통하여 프로그램의 비정상 종료를 막고 로그를 남길 수 있다.
시스템 오류(error): 가상 머신에서 발생하며 프로그래머가 처리할 수 없는 오류
ex) 동적 메모리가 없는 경우, 스택 오버 플로우 등
예외(Exception): 프로그램에서 제어할 수 있는 오류
ex) 읽어 들이려는 파일이 존재하지 않는 경우, 네트워크 연결이 끊어진 경우
모든 예외 클래스의 최상위 클래스는 Exception이다. 아래와 같이 다양한 예외 클래스가 제공된다. (아래 이미지에서는 예외 클래스 중 일부만을 보여준다.)
또한, JDK에서 제공되는 예외 클래스 외에 사용자가 필요에 의해 예외 클래스를 정의하여 사용할 수 있다. 기존 JDK 예외 클래스 중 가장 유사한 클래스에서 상속하는 형태로 정의할 수 있는데, 기본적으로 Exception에서 상속해도 된다.
사용자 정의 예외 클래스
public class IDFormatException extends Exception {
public IDFormatException(String message) {
super(message);
}
}
사용자 정의 예외 클래스 활용 예제
public class IDFormatTest {
private String userID;
public String getUserID() {
return userID;
}
public void setUserID(String userID) throws IDFormatException {
if (userID == null) {
throw new IDFormatException("아이디는 null일 수 없습니다.");
} else if (userID.length() < 8 || userID.length() > 20) {
throw new IDFormatException("아이디는 8자 이상 20자 이하로 쓰세요.");
}
this.userID = userID;
}
public static void main(String[] args) {
IDFormatTest idFormatTest = new IDFormatTest();
String userID = null;
try {
idFormatTest.setUserID(userID);
} catch (IDFormatException e) {
System.out.println(e);
}
userID = "abcdefg";
try {
idFormatTest.setUserID(userID);
} catch (IDFormatException e) {
System.out.println(e.getMessage());
}
}
}
출력 결과
메서드 'setUserID'에서 throw 명령어로 예외를 발생시키고, 해당 메서드에서는IDFormatException이 발생하지 않도록 throws 명령어를 통해 예외 발생을 지연시켰다. try-catch문에 대한 내용은 아래 활용 예제를 통해 알아보자.
try-catch-finally
try-catch문은 기본적으로 try{} 블록이 실행될 때 (catch에서 parameter로 받은 것과 같은) 에러가 나면 catch{} 블록이 실행되며 코드 실행이 중단되지 않도록 만드는 역할을 한다. 또한 뒤에 finally 명령어를 붙여주면 try{} 블록 -> finally{} 블록 -> (try{} 블록에서 에러가 난다면) catch{} 블록 순으로 실행된다.
아래는 파일을 불러올 때 try-catch-finally를 활용하여 예외 처리한 예제이다.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("c.txt");
} catch (FileNotFoundException e) {
System.out.println(e);
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("finally");
}
System.out.println("end");
}
}
try-with-resources
try-with-resources문은 리소스(file)를 자동 해제하도록 제공해주는 구문이다. 예를 들어 위 예제에서 close()를 명시적으로 호출하지 않아도 try{}블록에서 열린 리소스가 정상인 경우와 예외가 발생한 경우 모두 자동 해제된다. 이 구문을 사용하기 위해서는 해당 리소스에 AutoCloseable가 구현되어(implements) 있어야 한다. 예를 들어 FileInputStream 자바 내장 리소스 입력 클래스의 경우 AutoCloseable을 구현하고 있다.
아래는 파일을 불러올 때 try-with-resources문을 활용하여 예외 처리한 예제이다.
import java.io.FileInputStream;
import java.io.IOException;
public class AutoClosableTest {
public static void main(String[] args) {
try (FileInputStream fileInputStream = new FileInputStream("a.txt")) {
} catch (IOException e) {
System.out.println(e);
}
System.out.println("end");
}
}
또한, AutoClosable을 구현한 클래스를 직접 정의하여, 예외 처리 시 try-with-resources문에서 활용한 예제이다.
public class AutoCloseObj implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("close()");
}
}
public class AutoCloseTest {
public static void main(String[] args) {
AutoCloseObj obj = new AutoCloseObj();
try (obj) {
throw new Exception();
} catch (Exception e) {
System.out.println(e);
}
}
}
출력 결과
try{} 블록에서 에러가 나도, close()가 먼저 실행되는 모습이다.
throws
앞서 언급했듯이 throws를 활용하여 특정 method에서 특정 에러 발생을 지연시킬 수 있다.
아래는 throws를 활용하여 메서드에서 에러를 지연시키고, 실행 코드에서 한번에 예외처리를 하는 방식으로 코드를 작성한 예제이다.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ThrowsException {
public Class loadClass(String fileName, String className) throws FileNotFoundException, ClassNotFoundException {
FileInputStream fls = new FileInputStream(fileName);
Class c = Class.forName(className);
return c;
}
public static void main(String[] args) {
ThrowsException exception = new ThrowsException();
try {
exception.loadClass("b.txt", "java.lang.string");
} catch (FileNotFoundException e) {
System.out.println(e);
} catch (ClassNotFoundException e) {
System.out.println(e);
} catch (Exception e) {
System.out.println(e);
}
System.out.println("end");
}
}