컴파일러가 하는 일
- 구문체크 (e.g. 빨간 밑줄)
- 번역
- 최적화 (e.g. 상수 계산이나 생략된 코드 추가)
컴파일 에러 : 컴파일 할 때 발생하는 에러
런타임 에러 : 실행 할 때 발생하는 에러
논리적 에러 : 작성 의도와 다르게 동작
Java의 런타임 에러
에러 (Error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
예외 (Exceoption) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
예외처리
예외의 발생에 대비한 코드를 작성하는 것
프로그램의 비정상 종료를 막기 위해 !!
자바에서는 실행 시 발생할 수 있는 오류 (에러 와 예외) 를 클래스로 정의하였다.
Object 는 모든 클래스의 부모 클래스이기 떄문에 에러와 예외 클래스 역시 Object 클래스의 자식들이다.
Exception 클래스들 : 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
FileNotFoundException : 존재하지 않는 파일의 이름을 입력한 경우 발생
ClsssNotFoundException : 클래스의 이름을 잘못 적었을 경우 발생
DataFormatException : 입력한 데이터 형식이 잘못된 경우 발생
RuntimeException 클래스들 : 프로그래머의 실수로 발생하는 예외
ArrayIndexOutOfBoundsException : 배열의 범위를 벗어난 경우 발생
NullPointerException : 값이 null인 참조변수의 멤버를 호출하려 하는 경우 발생
ClassCastException : 클래스의 형변환을 잘못한 경우 발생
ArithmeticException : 정수를 0으로 나누려고 하는 경우 발생
try {
// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception1 e1) {
// Exception1 이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (Exception2 e2) {
// Exception2 이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
} catch (ExceptionN eN) {
// ExceptionN 이 발생했을 경우, 이를 처리하기 위한 문장을 적는다.
}
if문과 달리, try 블럭이나 catch 블럭 내에 포함된 문장이 하나뿐이어도 괄호 { } 생략 불가 ! ! !
1. 발생한 예외와 일치하는 catch 블럭이 있는지 확인한다.
2. 일치하는 catch 블럭을 찾게되면, 그 catch 블럭 내의 문장들을 수행하고 전체 try-catch 문을 빠져나간다.
만약 일치하는 catch 블럭이 없다면 예외는 처리되지 못한다.
1. catch 블럭을 거치지 않고 전체 try-catch 문을 빠져나간다.
if 문과 마찬가지로 try 블럭에서 예외가 발생하면 예외가 발생한 위치 이후에 있는 try 블럭의 문장들은 수행되지 않으므로 try 블럭에 포함시킬 코드의 범위를 잘 선택해야 한다.
예제
// 예외가 발생하지 않았을 때
class Ex8_1 {
public static void main(String args[]) {
System.out.println(1);
try {
System.out.println(2);
System.out.println(3);
} catch (Exception e) {
System.out.println(4); // 예외가 발생하지 않아서 수행되지 않음
} // try-catch의 끝
System.out.println(5);
}
}
// 결과
1
2
3
5
// 예외가 발생했을 때
class Ex8_2 {
public static void main(String args[]) {
System.out.println(1);
try {
System.out.println(0/0); // 예외 발생!!!
// 예외 발생 이후 try 블럭 문장들은
System.out.println(2); // 실행되지 않는다.
} catch (ArithmeticException ae) {
System.out.println(3);
} // try-catch의 끝
System.out.println(4);
} // main메서드의 끝
}
// 결과
1
3
4
try 블럭에서 예외가 발생하면, 발생한 예외를 처리할 catch 블럭을 찾는다.
첫번째 catch 블럭부터 순서대로 찾아 내려가며, 일치하는 catch 블럭이 없으면 예외는 처리되지 않는다.
예외의 최고 조상인 Exception을 처리하는 catch 블럭은 모든 종류의 예외를 처리할 수 있다.
(반드시 마지막 catch 블럭이어야 한다.)
// 결과
1
2
3
true
ArithmeticException
6
catch 문 수행후 바로 try-catch 문 빠져나오기 때문에 Exception 출력 안됨
마지막으로
아래에 후술하겠다.
자주 쓰이는 두가지이다.
printStackTrace() : 예외발생 당시의 호출스택(Call Stack)에 있었던 메소드의 정보와 예외 메시지를 화면에 출력한다.
getMessage() : 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.
...
try {
...
} catch (ExceptionA | ExceptionB e) { // 여러개의 catch 블럭을 |로 합칠 수 있다.
e.printStackTrace();
}
try {
...
// } catch (ParentException | ChildException e { // 에러!
} catch (ParentException e) { // Ok. 위의 라인과 의미상 동일
e.printStackTrace();
}
try (
...
} catch (ExceptionA | ExceptionB e) {
e.methodA(); // 에러! ExceptionA에 선언된 methodA()는 호출불가
if(e instanceof ExceptionA) {
ExceptionA 31 = (ExceptionA)e;
e1.methodA(); // Ok. ExceptionA에 선언된 메소드 호출가능
} else { // if(e instanceof ExceptionB)
...
정 사용하고 싶으면 if-else 문으로 형변환... 굳이
Exception e = new Exception("고의로 발생시켰음");
throw e;
class Ex8_6 {
public static void main(String args[]) {
try {
Exception e = new Exception("고의로 발생시켰음.");
throw e; // 예외를 발생시킴
// throw new Exception("고의로 발생시켰음.");
} catch (Exception e) {
System.out.println("에러 메시지 : " + e.getMessage());
e.printStackTrace();
}
System.out.println("프로그램이 정상 종료되었음.");
}
}
// 결과
에러메시지 : 고의로 발생시켰음
java.lang.Exception: 고의로 발생시켰음
at Ex8_6.main(Ex8_6.java:4)
프로그램이 정상 종료되었음.
e.printStackTrace가 스택의 내용을 찍어서 프로그램 비정상 종료때와 비슷함 하지만 정상 종료됨.
try에서 throw 로 던져서 catch 로 예외 처리했기 때문.
Exception과 자손
checked 예외 : 컴파일러가 예외 처리 여부를 체크 (예외처리 필수)
RuntimeException과 자손
unchecked 예외 : 컴파일러가 예외 처리 여부를 체크 안함 (예외처리 선택)
메소드가 호출시 발생가능한 예외를 호출하는 쪽에 알리는 것
구별하기!
예외를 발생시키는 키워드 throw
예외를 메소드에 선언할 때 쓰이는 throws
import java.io.*;
class Ex8_10 {
public static void main(String[] args) {
try {
File f = createFile(args[0]);
System.out.println( f.getName()+"파일이 성공적으로 생성되었습니다.");
} catch (Exception e) {
System.out.println(e.getMessage()+" 다시 입력해 주시기 바랍니다.");
}
} // main메서드의 끝
static File createFile(String fileName) throws Exception {
if (fileName==null || fileName.equals(""))
throw new Exception("파일이름이 유효하지 않습니다.");
File f = new File(fileName); // File클래스의 객체를 만든다.
// File객체의 createNewFile메서드를 이용해서 실제 파일을 생성한다.
f.createNewFile();
return f; // 생성된 객체의 참조를 반환한다.
} // createFile메서드의 끝
} // 클래스의 끝
예외가 발생한 메소드 내에서 자체적으로 처리해도 되는 것은 메소드 내에서 try-catch
문을 이용해서 처리하고,
메소드 내에서 자체적으로 해결이 안되는 경우에는 메소드에 예외를 선언해서 호출한 메소드에서 처리하도록 한다.
try 블럭 안에 return 문이 있어서 try 블럭을 벗어날 때도 finally 블럭이 실행된다
try {
// 예외가 발생할 가능성이 있는 문장들을 넣는다.
} catch (Exception e1) {
// 예외처리를 위한 문장을 적는다.
} finally {
// 예외 발생여부에 관계없이 항상 수행되어야 하는 문장들을 넣는다.
// finally 블럭은 try-catch 문의 마지막에 위치한다.
}
try {
startInstall();
copyFiles();
} catch (Exception e) {
e.printStackTrace();
} finally {
deleteTempFiles();
} // try-catch의 끝
class Ex8_12 {
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
System.out.println("main메서드에서 예외가 처리되었습니다.");
}
} // main메서드의 끝
static void method1() throws Exception {
try {
throw new Exception();
} catch (Exception e) {
System.out.println("method1메서드에서 예외가 처리되었습니다.");
throw e; // 다시 예외를 발생시킨다.
}
} // method1메서드의 끝
}