에러 (error) : 컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것
예외 (exception) : 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류
- 예외 처리 (Exception Handling)을 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되도록 할 수 있음
- 일반 예외 (Exception) : 컴파일러 체크 예외 / 자바 소스 파일을 컴파일하는 과정에서 예외 처리 코드가 필요한지 검사 > 예외 처리 코드가 없다면 컴파일 오류 발생
- 실행 예외 (Runtime Exception) : 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외
- JVM은 프로그램 실행 도중 예외가 발생하면 해당 예외 클래스로 객체를 생성하고 예외 처리 코드에서 예외 객체를 이용할 수 있도록함
- 모든 예외 클래스들은 java.lang.Exception 클래스를 상속 받음
1. 실행 예외 : 자바 컴파일러가 체크하지 않기 때문에 개발자의 경험에 의해 예외 처리 코드를 삽입해야 함
NullPointer Exception : 객체 참조가 없는 null 값을 갖는 참조 변수로 객체 접근 연산자인 도트 (.)를 사용했을 때 발생
ArrrayIndexOutOfBoundsException : 배열에서 인덱스 범위를 초과하여 사용할 경우 발생
- 실행 매개값이 없거나 부족할 경우 조건문을 이용해서 사용자에게 실행 방법을 알려주는 등, 배열값을 읽기 전에 배열의 길이를 먼저 조사하는 방법을 통해 예외를 처리함
NumberFormatException : 숫자로 변경 불가능한 문자열로 되어 있는 데이터를 숫자로 변경하는 경우 발생
| 반환 타입 | 메소드명(매개 변수) | 설명 |
| int | Integer.parseInt(String s) | 주어진 문자열을 정수로 변환해서 리턴 |
| double | Double.parseDouble(String s) | 주어진 문자열을 실수로 변환해서 리턴 |
- 매개값인 문자열이 숫자로 변환될 수 있다면 숫자를 리턴하지만, 숫자로 변환될 수 없는 문자가 포함되어 있다면 java.lang.NumberFormatException을 발생시킴
ClassCastException : 억지로 타입 변환을 시도할 경우 발생
- 상속 관계나 인터페이스와 구현 클래스간의 관계가 아닐 경우 다른 클래스로 타입 변환할 수 없음
- 타입 변환 전에 타입 변환이 가능한지 instanceof 연산자로 확인하여 예외를 처리함
2. 예외 처리 코드 : 프로그램에서 예외가 발생했을 경우 프로그램의 갑작스러운 종료를 막고, 정상 실행을 유지할 수 있도록 처리하는 코드
try-catch-finally 블록 : 생성자 내부와 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생할 경우 예외 처리를 가능하게 하는 구문
| 정상 실행 경우 | 예외 발생 경우 |
| try { // ① 예외 발생 가능 코드 } catch(예외클래스 e) { 예외 처리 } finally { // ② 항상 실행; } |
try { // ① 예외 발생 가능 코드 // 예외 발생 } catch(예외클래스 e) { // ② 예외 처리 } finally { // ③ 항상 실행; } |
- try 블록 : 예외 발생 코드가 위치 / 예외 발생 없이 정상 실행되면 catch 블록은 실행되지 않고 finally 블록을 실행하며 예외가 발생하면 즉시 실행을 멈추고 catch 블록 실행 후 finally 블록을 실행함
- finally 블록 : 예외 발생 여부와 상관없이 항상 실행할 내용이 있을 경우에만 작성 / try 블록과 catch 블록에서 return문을 사용하더라도 finally 블록은 항상 실행됨
3. 예외 종류에 따른 처리 코드
다중 catch
- 발생되는 예외별로 예외 처리 코드를 다르게 하여 예외 처리 가능
- catch 블록이 여러 개지만 단 하나의 catch 블록만 실행됨
- 다중 catch 블록 작성 시 상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 함
멀티 catch
- 하나의 catch 블록에서 여러 개의 예외를 처리 가능하게 함
- catch 괄호 () 안에 동일하게 처리하고 싶은 예외를 |로 연결
public class Example {
public static void main(String[] args) {
try {
String data1 = args[0];
String data2 = args[1];
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + " + " + data2 + " + " + result);
} catch(ArrayIndexOutOfBoundsException | NumberFormatException e) { // 멀티 catch
System.out.println("실행 매개값의 수가 부족하거나 숫자로 변환할 수 없습니다..");
} catch(Exception e) { // 다중 catch
System.out.println("알수 없는 예외 발생");
} finally {
System.out.println("다시 실행하세요.");
}
}
}
4. 자동 리소스 닫기
try-with-resources : 예외 발생 여부와 상관없이 사용했던 리소스 객체의 close() 메소드를 호출하여 안전하게 리소스를 닫음
리소스 : 각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널 등의 데이터를 읽고 쓰는 객체
FileInputStream = null;
try {
fis = new FileInputStream("File.txt");
···
} catch(IOException e) {
···
} finally {
if (fis != null) {
try {
fis.close();
} catch(IOException e) {}
}
}
위와 같은 코드를 아래와 같이 줄일 수 있음
try(FileInputStream fis = new FileInputStream("File.txt")) {
···
} catch(IOException e) {
···
}
- close()를 명시적으로 호출한 곳이 없음
- try 블록이 정상적으로 실행됐거나 도중에 예외가 발생하게되면 자동으로 FileInputStream의 close() 메소드가 호출됨
- try 블록에서 예외가 발생하면 우선 close()로 리소스를 닫고 catch 블록을 실행
사용 조건 : 리소스 객체가 close() 메소드가 정의되어 있는 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 함
5. 예외 떠넘기기
경우에 따라 메소드를 호출한 곳으로 예외를 떠넘기는 방법
throws 키워드 : 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘기는 역할을 함 / 떠넘길 예외 클래스를 수미표로 구분해서 나열
- throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야하며 catch 블록에서 떠넘겨 받은 예외를 처리해야 함
- main() 메소드에서도 throws 키워드로 예외를 떠넘길 수 있는데, 이는 JVM이 최종적으로 예외 처리를 하게 됨
리턴타입 메소드명(매개변수, ···) throws Exception {
}
위와 같은 형식으로 모든 예외를 간단히 떠넘길 수 있음
public class Example {
public static void main(String[] args) {
try {
findClass();
} catch(ClassNotFoundException e) {
System.out.println("클래스가 존재하지 않습니다.");
}
}
public static void findClass() throws ClassNotFoundException {
Class clazz = Class.forname("java.lang.String2");
}
}
- forName() 메소드는 선언부 뒤에 throws ClassNotFoundException이 붙어 있기 때문에 forname() 메소드를 호출할 때 try-catch 블록으로 예외를 처리하거나, throws로 예외를 떠넘겨야 함
6. 사용자 정의 예외
자바 표준 API에서 제공하는 예외 클래스 이외에 개발자가 직접 정의해서 만들어야 하는 예외 클래스
예외 클래스 선언
- 일반 예외(컴파일러가 체크)로 선언시 Exception 상속, 실행 예외(컴파일러가 체크 X)로 선언시 RuntimeException 상속
- 클래스 이름이 Exception으로 끝나는 것이 좋음
- 필드, 생성자, 메소드 선언들을 포함할 수 있지만, 대부분 생성자 선언만 포함
- 생성자는 두 개 선언이 일반적 : 매개 변수 없는 일반 기본 생성자 + 예외 발생 원인을 전달하기 위한 String 타입의 매개 변수
- String 타입의 매개 변수를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨줌
public class BalanceInsufficientException extends Exception {
public BalanceInsufficientException() {}
public BalanceInsufficientException(String message) {
super(message);
}
}
예외 발생 시키기
throw new XXXException();
throw new XXXException("메시지");
- 에외 발생 코드를 가지고 있는 메소드는 내부에서 try-catch 블록으로 예외 처리 가능
- 대부분 자신을 호출한 곳에서 예외를 처리하도록 throws 키워드로 예외를 떠넘김
public class Account {
private long balance;
public Account() {}
public long getBalance() {
return balance;
}
public void deposit(int money) {
balance += money;
}
public void withdraw(int money) throws BalanceInsufficientException {
if (balance < money) {
throw new BalanceInsufficientException("잔고부족 : " + (money-balance) + " 모자람");
}
balance -= money;
}
}
7. 예외 정보 얻기
try 블록에서 예외가 발생하면 예외 객체는 catch 블록의 매개 변수에서 참조하게 되므로 매개 변수를 이용하면 예외 객체의 정보를 알 수 있음
- 모든 예외 객체는 Exception 클래스를 상속하기 때문에 Exception이 가진 모든 메소드들은 모든 예외 객체에서 호출 가능
- 예외 발생 시 String 타입의 메시지를 갖는 생성자를 이용했다면, 메시지는 자동적으로 예외 객체 내부에 저장됨
- 예외 메시지의 내용에는 왜 예외가 발생했는지에 대한 간단한 설명이 포함되어야 함 > 예외 코드로 세분화 할 수도 있음
- 주로 getMessage()와 printStackTrace() 메소드를 많이 사용함
try {
···
} catch(XXXException e) {
// 예외가 가지고 있는 Message 얻기
String message = e.getMessage();
// 예외의 발생 경로를 추적
e.printStackTrace();
}
'언어 > Java' 카테고리의 다른 글
| 중첩 클래스와 인터페이스 (0) | 2023.08.29 |
|---|---|
| 인터페이스 (1) | 2023.08.27 |
| 클래스의 타입 변환과 다형성 (0) | 2023.08.27 |
| 메소드 재정의 (오버라이딩) (0) | 2023.08.24 |
| 상속 (0) | 2023.08.24 |