try-catch-finally문을 사용한다
try{
//예외가 발생될만한 코드
}catch(FileNotFoundException e){ //FileNotFoundException이 발생했다면
}catch(IOException e){ //IOException이 발생했다면
}catch(Exception e){ //Exception이 발생했다면
}finally{
///어떤 예외가 발생하던 말던 무조건 실행
}
//만약 정수를 입력받아야 하는데 다른 값이 들어온다면?
while(true) {
try {
System.out.println("곱하고자 하는 두 수 입력 >> ");
int n = scan.nextInt();
int m = scan.nextInt();
System.out.println(n + "x" + m + "=" + n*m);
}catch (Exception e) {
// TODO: handle exception
System.out.println("실수는 입력할 수 없습니다");
break;
}
}
String[] stringNumber = {"23","12","3.141592","998"};
int i=0;
try {
for(i=0; i<stringNumber.length; i++) {
int j = Integer.parseInt(stringNumber[i]);
System.out.println("숫자로 변환된 값은: "+j);
}
}catch (NumberFormatException e) {
// TODO: handle exception
System.out.println(stringNumber[i]+"는 정수로 변환할 수 없습니다");
}
InputMismatchException
을 사용한다.scan은 입력값을 버퍼에 저장하는데 만약 다른 값이 들어온 채로 catch문을 실행하게 되면 버퍼 안에 exception에 대한 입력값이 남아있게 된다. 그래서 catch문에서 버퍼에 들어있는 입력값을 지우기 위해 scan.next( )
를 사용한다. scan.next( )를 사용하게 되면 키보드 입력을 받은 버퍼의 내용을 빼서 이것을 변수에 할당하면 그 변수 안에 값이 들어가는 형식으로 사용하는데 변수에 할당하지 않고 그냥 사용하면 버퍼안에 있는 값을 버리는 개념으로 사용된다.
Scanner scan = new Scanner(System.in);
System.out.println("정수 3개 입력하세요");
int sum = 0, n=0;
for (int i=0; i<3; i++) {
System.out.println(i+">>");
try { //예외가 실행될 수 있는 코드를 작성.
n = scan.nextInt();
}catch (InputMismatchException e) { //예외가 들어왔을 시 실행되는 코드
// TODO: handle exception
System.out.println("정수가 아닙니다. 다시 입력하세요");
scan.next();
//입력 스트림에 있는 정수가 아닌 토큰을 버림. 키보드의 값을 버퍼에 임시로 저장하는데
//내가 잘못 저장한 것을 버려야 하기 때문에 사용한다
//만약 scan.next()를 쓰지 않으면 다음 입력받을 때 내가 잘못입력한 값 + 정수값이 버퍼에
//저장되기 때문에 또 exception이 발생한다. 이것을 방지하기 위해 버퍼에 있는 값을 버린다.
//scan.next()는 키보드의 담긴 값을 가져온다는 의미이기 때문에 어느 변수에 저장되지
//않으면 버퍼안에 있는 입력 내용이 사라진다.
i--; //인덱스 증가하지 않도록 미리 감소
continue; //다음루프 시작. 근데 이미 i의 값을 1 감소시켜서 다시 실행됨
}
sum += n;
}
JDBC 드라이버를 통해 DB에 접근하거나 파일을 열고 데이터를 읽어오는 경우
FileInputStream inputStream = null;
try {
File file = new File("./tmp.txt");
byte[] buffer = new byte[512]; // 파일을 열고 512 바이트 버퍼 생성
inputStream = new FileInputStream(file);
inputStream.read(buffer, 0, 512);
// 파일로부터 512바이트만큼 데이터를 읽은 후
inputStream.close();
// 파일 스트림을 닫는 코드.
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
}
만약 IOException이 발생한다면 프로그램은 에러 로그를 찍고 진행하지만 파일 스트림은 열려있는 채로 남아있다. 만약 초당 수백명의 사용자가 접속해 사용하는 시스템이라면, 정리되지 않은 리소스가 문제를 일으킬 가능성이 있다.
따라서, try-catch로 리소스를 열었다면, finally 블록에서 리소스를 정리해야한다.
FileInputStream inputStream = null;
try {
File file = new File("./tmp.txt");
byte[] buffer = new byte[512];
inputStream = new FileInputStream(file);
inputStream.read(buffer, 0, 512);
inputStream.close();
} catch (FileNotFoundException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
log.error(e);
}
}
메소드를 정의할 때, 이 메소드에서 발생할 수 있는 예외를 throw 구문으로 명시할 수 있다.
이 때, 메소드에서 발생할 수 있는 예외는 최대한 자세히 예외를 명시하는 것이 좋다.
public void exceptableMethod() throw Exception;
public void execptableMethod() throw NumberFormatException;
첫번째 경우, Exception으로 퉁쳐버리면 이 메소드를 호출하는 쪽에서 예외처리하는 코드가 복잡해진다. 따라서 모든 경우에 대한 코드를 작성할 수 밖에 없다.
/**
* 이 메소드는 어떨때 사용하는 메소드입니다.
*
* @param input 입력 값
* @throws MyException 어떤어떤 경우에 이 예외가 발생합니다.
*/
public void myMethod(String input) throws MyException { ... }
javadoc 문서를 통해서 기술할 수 있다. 어떤 상황에서 예외가 발생할 수 있는지 글로 자세히 설명해줘야 메소드를 호출하는 쪽에서 적당한 예외코드를 작성할 수 있다. @throw 선언으로 설명하는 글을 작성하자.
try-catch 블럭에서 여러 예외가 발생할 수 있는 경우 좀 더 상세한 예외부터 처리해야 한다. 예외가 상속관계에 있을 경우 앞쪽 catch 절에서 더 넓은 범위의 예외를 먼저 처리해버리면, 뒤 쪽 catch 절이 쓸모없게 된다.
try {
method();
} catch (Exception e) {
log.error(e);
} catch (IllegalArgumentException e) {
// 실행 안 됨
log.error(e);
}
이렇게 처리하면 모든 예외가 Exception 절에서 걸려서 IllegalArgumentException 처리 블록은 실행이 안됨.
try {
method();
} catch (IllegalArgumentException e) {
log.error(e);
} catch (Exception e) {
log.error(e);
}
따라서 순서를 상세한 예외부터 처리해야한다.