우리는 사용자의 프로그램 오작동이든, 시스템 내부의 예기치 못한 오류든 많은 경우의 예외 사항을 어플리케이션을 운영 하면서 맞게 됩니다. 그러므로 우리는 예외 처리에 대해서 알 필요가 있습니다.
예를 들어 보겠습니다. 만약 계산기 프로그램을 만든다고 가정 하였을 때, 사용자가 어떤 수를 0으로 나누려고 한다면 에러가 발생 할 것 입니다. 다음과 같이 말이죠.
public class Main {
static public void main(String[] args) {
int c = 5 / 0;
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.main(Main.java:3)
위 에러를 잘 읽어보면, java.lang.ArithmeticException
이 발생 했다는 것을 알 수 있습니다. 우리는 **C++**에서 예외 처리를 위해서 try
, catch
를 사용 한 것을 알고 있습니다. Java도 마찬 가지로, try
, catch
문을 이용하여 예외 처리를 할 수 있습니다. 다음과 같이 말이죠.
public class Main {
static public void main(String[] args) {
try {
int c = 5 / 0;
} catch (ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.");
}
}
}
0으로 나눌 수 없습니다.
우리는 위와 같이 try
블럭에 예외처리를 시행 할 코드를 삽입 하고, catch
문에 예외가 발생할 것으로 예상 되는 Exception
클래스를 catch
문에 넣으면 됩니다. 그런데 우리가 예상치 못한 Exception
클래스가 throw
(발생) 하면, 우리는 어떻게 해야 할 까요? 우리가 **C++**에서 배웠듯, Exception
클래스들은 Exception
이라는 클래스를 상속 받은 클래스 입니다. 그러므로, 우리는 전에 배웠던, 클래스의 다형성을 이용하여, 다양한 타입의, 예상치 못했던 Exception
들을 일괄 처리 할 수 있습니다.
public class Main {
static public void main(String[] args) {
try {
int[] a = {1, 2, 3};
System.out.println(a[3]);
} catch (ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.");
} catch (Exception e) {
System.out.println(e.getClass().getName());
System.out.println("알 수 없는 에러가 발생 했습니다.");
}
}
}
java.lang.ArrayIndexOutOfBoundsException
알 수 없는 에러가 발생 했습니다.
예외가 처리 되든, 처리가 되지 않든 꼭 실행을 원하는 코드 블럭이 있을 수 있습니다. 그럴 때는 finally
블럭을 이용하여, 해당 블럭을 무조건 실행 하게 할 수 있습니다. 이 문구는, try
문에서 return
이 되더라도, 무조건 실행 됩니다.
public class Main {
static public void main(String[] args) {
try {
int[] a = {1, 2, 3};
return;
} catch (ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.");
} catch (Exception e) {
System.out.println(e.getClass().getName());
System.out.println("알 수 없는 에러가 발생 했습니다.");
} finally {
System.out.println("Finally 구문 실행.");
}
}
}
Finally 구문 실행.
우리는 Exception
클래스에 있는 printStackTrace()
와 getMessage()
를 통해 예외 처리가 일어 나게 된 과정과, 메시지를 확인 할 수 있습니다.
printStackTrace()
: 예외 발생 시점에 호출 스택에 있었던 메서드들의 정보 및 예외 메시지를 화면에 출력합니다.getMessage()
: 발생한 Exception
클래스의 인스턴스에 저장된 메시지를 얻을 수 있습니다.public class Main {
static public void main(String[] args) {
try {
throw new Exception("에러가 났지롱");
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
java.lang.Exception: 에러가 났지롱
at Main.main(Main.java:4)
에러가 났지롱
우리는 **C++**에서 했던 것 처럼, Exception
을 상속 받은, 다른 예외 클래스를 만들 수 있습니다. 하지만, 우리는 용도에 따라서, 다른 Exception
을 상속 해야 합니다.
Exception
: 사용자의 실수와 같은 외적인 요인으로 발생 하는 예외, 컴파일 시점에서 예측 가능한 예외들이 이에 속합니다. (예: IOException
, ClassNotFoundException
)RuntimeException
: 프로그래머의 실수로 발생하는 예외 상황, 논리적 오류에 속하는 예외들이 이에 속합니다. (예: ArithmeticException
, ClassCastException
)구조는 RuntimeException
이 Exception
을 상속 받은 구조 입니다.
출처: https://commons.wikimedia.org/wiki/File:Java_exception_classes.svg
우리는 Exception
을 상속 받아서 또 다른 Exception
클래스를 만들고, 사용자에게 원치 않는 예외 입력를 받았을 때, Exception
을 throw
할 수 있습니다. 생성자에서 super()
를 이용하여, Exception
클래스의 생성자를 호출, 메시지를 생성 할 수 있습니다.
import java.util.Scanner;
class NoneInputException extends Exception {
public NoneInputException(String s) {
super(s);
}
}
public class Main {
static public void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
System.out.print("아무거나 입력 후, Enter를 눌러 주세요: ");
String s = scanner.nextLine();
if (s.equals("")) {
throw new NoneInputException("아무 것도 없지롱");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
아무거나 입력 후, Enter를 눌러 주세요:
아무 것도 없지롱
NoneInputException: 아무 것도 없지롱
at Main.main(Main.java:18)
JDK1.7 이상부터 지원하는 기능입니다. try
문 옆에 괄호로 close()
가 필요한 스트림 클래스 같은 경우에, try-catch
문이 끝나면, 스트림을 자동으로 닫아주는 기능을 수행 합니다.
test.txt
1
2
3
4
5
Main.java
import java.io.*;
public class Main {
static public void main(String[] args) {
try (FileReader fr = new FileReader("test.txt")) {
while (true) {
int temp = fr.read();
if (temp == -1)
throw new EOFException();
System.out.print((char)temp);
}
} catch (FileNotFoundException e) {
System.out.println("파일이 없습니다.");
} catch (EOFException e) {
System.out.println("\n읽기 끝!");
} catch (IOException ie) {
ie.printStackTrace();
}
}
}
1
2
3
4
5
읽기 끝!
함수에 throws
문을 입력하여, 특정 예외 클래스가 나올 것이라고 타입을 지정 해 줄 수 있습니다. 이렇게 예외 타입을 지정을 하게 되면 다음과 같은 이점을 얻을 수 있습니다.
throw
되지 않는 예외 타입을 catch
하려는 경우에 컴파일 에러를 발생 시킵니다.throws
로 예외 타입이 지정된 해당 함수가 try
, catch
문 안에 없는 경우 에러를 발생 시킵니다. 즉, 무조건 예외 처리 하게 끔 만들 수 있습니다.throw
해줘야 하는 에러를 알려주어, 의사소통을 원활하게 할 수 있습니다.public class Main {
static public void main(String[] args) {
try {
someThingForThrow();
} catch (NullPointerException ne) {
ne.printStackTrace();
} catch (ArithmeticException ae) {
ae.printStackTrace();
}
}
public static void someThingForThrow() throws NullPointerException, ArithmeticException {
double temp = Math.random();
if (temp >= 0.5) {
throw new NullPointerException();
} else {
throw new ArithmeticException();
}
}
}
java.lang.ArithmeticException
at Main.someThingForThrow(Main.java:17)
at Main.main(Main.java:4)
다음 시간에는 기본 import
되는 클래스인 java.lang
패키지에 대해서 공부 해 보는 시간을 가져 보도록 하겠습니다.