자바의 예외 처리에 대해 학습하세요.
자바에서는 프로그램이 실행되는 도중 발생하는 예외를 처리하기 위해 try / catch / finally 문을 사용한다.
try {
// 예외 발생시 throw 하고자 하는 코드
catch(XXXException e1) {
// e1 예외가 발생할 경우에 실행될 코드
}
catch(XXXException e2) {
// e2 예외가 발생할 경우에 실행될 코드
}
...
finally {
// 예외 발생과 상관없이 무조건 실행할 코드
}
}
try
블럭에는 여러 개의 catch
블록이 올 수 있으며, 발생하는 예외의 종류와 일치하는 단 한 개의 catch
블록만 수행된다.try
블록에서 예외가 발생하면 throw
키워드를 사용하여 예외가 던져진다. 던져진 예외는 catch 블록에 의해 잡힐 수 있다. catch
블록에서는 발생한 예외 코드나 예외 객체를 전달 받아 그 처리를 담당한다. 다중 catch문으로 작성할 때의 주의할 점은 범위가 낮은 예외들부터 작성을 해야 한다는 것이다(예외는 상속 관계를 가지고 있기 때문이다.).finally
키워드는 예외와 별개로 반드시 실행시키고 싶은 소스들을 처리하고 싶을 때 사용한다. try {
// 예외 발생시 trhow 하고자 하는 코드
} catch(XXXException e1) {
// 예외 발생시 실행될 코드
try {
// 예외 발생시 throw 하고자 하는 코드
catch(XXXException e2) {
// 예외 발생시 실행될 코드
}
}
}
catch 블록 내에 다시 try-catch문을 사용할 수 있는데 이 때 주의할 점은 참조되는 변수의 이름이 중복되면 안된다.
printStackTrace()
, getMessage()
예외가 발생하였을 때 해당 인스턴스에는 발생한 예외의 정보들이 담겨져 있다. 이에 대해 알고 싶을 때
printStackTrace()
,getMessage()
를 사용하여 예외의 정보를 얻는다.
printStackTrace()
getMessage()
throw 키워드를 사용하면 고의로 예외를 발생시킬 수 있다.
class User {
int age;
}
class UserException {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
User user = new User();
System.out.println("나이를 입력하세요");
user.age = sc.nextInt();
try {
if(user.age < 0 || user.age > 120) {
throw new IllegalArgumentException("정확한 값을 입력해주세요");
}
} catch(IllegalException e) {
System.out.println(e.getMessage());
}
}
}
IllegalArgumentException 인스턴스를 생성하여 throw로 예외를 발생시켰다.
throws 키워드를 통해 메서드에 예외를 선언할 수도 있다.
public void 예외_던져줭() throws Exception {
// 메서드 내용
}
예외_던져줭()
메서드에 예외를 선언해두고 해당 메서드를 호출할 때 어떤 예외를 처리해야 되는지 알려준다.
,
를 기준으로 여러개 선언도 가능하다.
public void 예외_많이_던져줭 throws Exception1, Exception2, Exception3 {
// 메서드 내용
}
throws 자체는 예외의 처리와는 관계가 없다. 메서드를 호출한 호출자가 이 예외를 처리해야 하기 떄문이다.
1.7부터 자원의 해제를 자동으로 해주는 try-with-resources
구문이 추가되었다. resources
는 DataSource, Connection, FileInpuStream 등 사용이 끝날 때 닫아줘야 하는 자원을 말한다.
try-with-resources
는 try 키워드 {}
이전에 ()
를 열어 소괄호에 사용하게 될 자원을 입력하여 사용한다. 모든 자원이 ()
에 들어갈 수 있는 것은 아니고 AutoCloseable
인터페이스를 구현한 객체만 사용 가능하다.
public static void main(String[] args) {
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
MysqlDataSource ds = getMySQLDataSource();
try {
con = ds.getConnection();
pst = con.prepareStatement("SELECT VERSION()");
rs = pst.executeQuery();
if (rs.next()) {
String version = rs.getString(1);
System.out.println(version);
}
} catch (IOException e) {
...
} catch (SQLException e) {
...
} finally {
if (rs != null) {
rs.close();
}
if (pst != null) {
pst.close();
}
if (con != null) {
con.close();
}
}
}
}
public static void main(String[] args) {
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
MysqlDataSource ds = getMySQLDataSource();
try(con = ds.getConnection();
pst = con.prepareStatement("SELECT VERSION()");
rs = pst.executeQuery()) {
if (rs.next()) {
String version = rs.getString(1);
System.out.println(version);
}
} catch (IOException e) {
...
} catch (SQLException e) {
...
}
}
}
출처 : https://www.javamadesoeasy.com/2015/05/exception-handling-exception-hierarchy.html
Checked Exception
컴파일 시점에서 확인될 수 있는 예외. 만약 코드 내에서 이 예외를 발생시킨다면 해당 예외는 반드시 처리되거나 해당 코드가 속한 메서드 선언부에 예외를 선언해줘야 한다.
치명적인 예외 상황을 발생시키기 때문에 try-catch문으로 감싸줘야 한다. 예외가 발생할 가능성이 있는 구문을 예외처리하지 않았다면 컴파일 에러가 발생한다.
Unchecked Exception(Runtime Exception)
컴파일 단계에서 확인되지 않는 예외이다. Java에서는 Runtime Exception과 그 하위 클래스 ,그리고 Error와 그 하위 클래스가 이에 속한다. 이 예외들은 개발자가 알아서 처리 해야한다.
try / catch 보다는 해당 예외가 발생하지 않도록 주의하여 코드를 짜는 것이 좋다.
예외와 에러의 가장 큰 차이는 System Level의 문제와 Application Level의 문제의 차이다.
프로그램을 사용하다가 프로그램이 비정상적으로 종료될 때 이러한 결과를 초래하는 원인
Error는 빌트인 클래스 Throwable의 서브 클래스이다. 에러는 근본적으로 JVM에서 생성되거나 나타나는 예외이다. 오류는 일반적으로 프로그램에서 처리할 수 없는 치명적인 오류로 인해 발생한다. 프로그램에서 처리할 수 없다는 뜻은 사용자가 제어할 수 없다는 뜻이다.
컴파일러는 발생에 대한 지식이 없으므로 오류는 항상 검사되지 않은 유형이다. 오류는 항상 런타임 환경에서 발생한다. 예를 들면 스택 오버플로우, 메모리 부족, 시스템 충돌 등이 해당한다. 이러한 종류의 오류는 시스템으로 인한 것이다. 오류가 발생하면 프로그램이 비정상적으로 종료된다.
따라서 에러는 기본적으로 Unchecked Exception
이다.
발생하더라도 프로그래머가 미리 적절한 코드를 사용해서 프로그램이 비정상적으로 종료되지 않도록 핸들링 해줄 수 있다.
예외는 내장 클래스 Throwable의 하위 클래스이기도 하다. 예외는 런타임 환경에서 발생하는 예외적인 조건이다. 예외의 대부분은 프로그램의 코드로 인해 발생하지만 예외는 복구할 수 있으므로 프로그램 자체에서 예외를 처리할 수 있다. 예외는 3개의 키워드 try
, catch
, throw
를 사용하여 처리한다.
비교 | 오류 | 예외 |
---|---|---|
기본 | 시스템 자원이 부족하여 오류가 발생함 | 코드로 인해 예외가 발생함 |
회복 | 오류는 복구할 수 없다. | 예외는 복구할 수 있다. |
키워드 | 프로그램 코드에서 오류를 처리할 수 있는 방법은 없다. | 예외는 3개의 키워드 try , catch , throw 를 사용하여 처리된다. |
결과 | 오류가 감지되면 프로그램이 비정상적으로 종료된다. | 예외가 감지되면 throw 및 catch 키워드에 따라 예외가 발생한다. |
유형 | 오류는 검사되지 않은 유형으로 분류된다. | 예외는 체크되거나 확인되지 않은 유형으로 분류 된다. |
패키지 | Java에서 오류는 java.lang.Error 패키지로 정의된다. | Java에서 예외는 java.lang.Exception 패키지로 정의된다. |
- 시스템 자원이 부족한 경우에만 오류가 발생하지만 코드에 문제가 있는 경우 예외가 발생한다.
- 오류는 복구할 수 없지만 예외를 처리할 코드를 준비하면 예외를 복구할 수 있다.
- 오류는 결코 처리할 수 없지만 예외를 throw하는 코드가 try 및 catch 블록 내에 작성된 경우 예외가 코드에 의해 처리될 수 있다.
- 오류가 발생하면 프로그램이 비정상적으로 종료된다. 반면에 예외가 발생하면 프로그램은 예외를 throw하고 try & catch 블록을 사용하여 처리된다.
- 오류는 검사되지 않은 유형이다. 오류는 컴파일러에 대한 지식이 아니지만 예외는 검사된 것과 검사되지 않은 것으로 분류된다.
- 일반 예외로 선언할 경우 Exception 을 상속받아 구현한다.
- 실행 예외로 선언할 경우 RuntimeException을 상속받아 구현한다.
Checked Exception
)와 컴파일러가 체크하지 않는(Unchecked Exception
)로 선언할 수 있다.{}
블록의 예외 처리에서 이용하기 위함이다.public class CustomException extends RuntimeException {
// 1. 매개 변수가 없는 생성자
public CustomException() {
}
// 2. 예외 발생 원인을 전달하기 위해 String 타입의 매개변수를 갖는 생성자
public CustomException(String message) {
super(message); // RuntimeException 클래스의 생성자를 호출한다.
}
}
public static void main(String[] args) {
try {
test();
} catch(CustomException e) {
System.out.println("아ㅋㅋ 예외라고");
System.out.println(e.getMessage());
}
}
public static void test() throws CustomException {
throw new CustomException("ㅋㅋ아 예외라고");
}