예외처리는 프로그램 실행 중 발생할 수 있는 오류 상황에 적절히 대응하여 프로그램이 비정상 종료되지 않도록 도와주는 기술입니다.
💡 비유: 예외처리는 자동차의 에어백처럼, 사고(오류)가 나더라도 프로그램을 안전하게 보호해 줍니다.
try {
// 예외 가능성이 있는 코드
} catch (예외타입 변수명) {
// 예외 발생 시 실행될 코드
}
Scanner scanner = new Scanner(System.in);
String[] greetings = {"안녕하세요.", "반갑습니다.", "또 오세요."};
while (true) {
try {
System.out.print("0 이상 " + greetings.length + " 미만의 정수를 입력하세요 (99는 종료): ");
if (scanner.hasNextInt()) {
int index = scanner.nextInt();
if (index == 99) break;
System.out.println(greetings[index]);
} else {
System.out.println("정수를 입력하세요.");
scanner.next(); // 잘못된 입력 제거
}
} catch (Exception e) {
System.out.println("⚠ 예외 발생: " + e.getMessage());
}
}
finally는 예외 발생 여부와 관계없이 항상 실행되는 블록입니다.
try {
// 예외 가능 코드
} catch (Exception e) {
System.out.println("예외 발생");
} finally {
System.out.println("항상 실행됩니다");
}
try {
int result = divide(a, b);
} catch (ArithmeticException ae) {
System.out.println("0으로 나누는 오류");
} catch (Exception e) {
System.out.println("기타 예외");
}
catch (NullPointerException | IOException e) {
System.out.println("예외 처리");
}
🔑 멀티 캐치는 서로 관련 없는 예외를 하나의 처리 블록으로 묶을 때 유용합니다.
IOException, SQLExceptionNullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException🔍 비유: Checked 예외는 신호등처럼 반드시 지켜야 하는 규칙, Unchecked 예외는 실수나 방심으로 인한 사고입니다.
메서드에서 예외가 발생할 수 있음을 호출자에게 알림
public static void printGreeting(int index) throws ArrayIndexOutOfBoundsException {
System.out.println(greetings[index]);
}
개발자가 명시적으로 예외 객체를 발생시킴
if (success == 0) {
throw new ServerTimedOutException("서버 연결 실패", 80);
}
개발자가 상황에 맞게 직접 예외 클래스를 정의하여 사용할 수 있습니다.
public class ServerTimedOutException extends Exception {
private int port;
public ServerTimedOutException(String message, int port) {
super(message);
this.port = port;
}
public int getPort() {
return port;
}
}
부모 클래스의 메서드를 오버라이딩할 때는 같거나 더 좁은 범위의 예외만 throws 가능합니다.
🔒 부모보다 더 위험한 예외를 던질 수는 없습니다.
// FileSystem.java
import java.io.IOException;
public class FileSystem {
public void save(int num) throws IOException {
if (num < 0) {
throw new IOException("num이 0보다 작습니다.");
}
System.out.println("File에 저장됐습니다.");
}
}
// Super.java
import java.io.IOException;
public class Super {
FileSystem fs = new FileSystem();
public void doIt(int num) throws IOException {
System.out.println("Super.doIt");
fs.save(num);
}
}
// Database.java
import java.sql.SQLException;
public class Database {
public void insert(int num) throws SQLException {
if (num > 100) {
throw new SQLException("num이 너무 큽니다.");
}
System.out.println("데이터베이스에 저장되었습니다.");
}
}
// Sub.java
import java.io.IOException;
import java.sql.SQLException;
public class Sub extends Super {
Database db = new Database();
@Override
public void doIt(int num) throws IOException {
System.out.println("Sub.doIt");
try {
db.insert(num);
} catch (SQLException e) {
throw new IOException(e.getMessage());
}
}
}
// ThrowsOverridingExample.java
import java.io.IOException;
import java.util.Random;
public class ThrowsOverridingExample {
public static void main(String[] args) {
Super[] works = new Super[2];
works[0] = new Super();
works[1] = new Sub();
Random rand = new Random();
int data = rand.nextInt(200);
for (Super s : works) {
try {
s.doIt(data);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
🔁 Sub 클래스는 Super의 예외보다 더 넓은 SQLException을 throws할 수 없기 때문에, catch로 감싸고 IOException으로 변환합니다.
| 항목 | 설명 |
|---|---|
| try-catch-finally | 예외를 안전하게 처리하는 기본 구조 |
| 다중 catch / 멀티 캐치 | 다양한 예외를 개별 또는 통합 처리 가능 |
| Checked vs Unchecked | 컴파일러가 강제하는 예외 여부에 따른 분류 |
| throw / throws | 예외를 직접 발생시키거나 전달하는 방식 |
| 사용자 정의 예외 | 상황에 맞는 맞춤형 예외 처리 |
| 오버라이딩 시 예외 제한 | 부모보다 더 넓은 예외는 throws할 수 없음 |
✅ 예외처리는 프로그램의 복원력을 높이는 핵심 기술입니다. 예외 상황을 예측하고 안전하게 처리함으로써 사용자 경험을 개선할 수 있습니다.