1. 프로그램의 비정상 종료 방지
2. 에러 발생 시의 적절한 대응
3. 코드의 가독성 및 유지보수성 향상
4. 프로그램의 신뢰성 향상
5. 디버깅 및 문제 해결에 도움
6. 프로그램 흐름 제어
Throwable
클래스를 중심으로 계층 구조를 형성하고 있습니다. 이 계층 구조는 크게 두 가지 주요 하위 클래스인 Error
와 Exception
으로 나눌 수 있으며, 각각의 용도와 처리 방법이 다릅니다.Throwable
, Error
, Exception
, RuntimeException
, 등 자바에서 기본적으로 제공하는 예외 클래스들은 java.lang
에 소속되어 있기 때문에, 별도의 import
구문 없이 사용하실 수 있습니다.Throwable
클래스Error
, Exception
Error
클래스Error
는 프로그램의 비정상적인 상태를 나타내므로, 이를 처리하기 위해 예외 처리 코드를 작성하는 것은 권장되지 않습니다. Error
가 발생하면 프로그램을 종료하고 로그를 남기는 것이 일반적입니다.Exception
클래스RuntimeException
, IOException, SQLException, 등Exception
의 하위 클래스는 크게 두 종류로 분류할 수 있습니다.RuntimeException
클래스RuntimeException
을 제외한 모든 하위 클래스RuntimeException
클래스RuntimeException
과 그 하위 클래스들은 체크되지 않은 예외(unchecked exception
)입니다. 컴파일러가 예외 처리 여부를 검사하지 않으며, 개발자가 선택적으로 처리할 수 있습니다.RuntimeException
은 주로 프로그래머의 실수에 의해 발생하는 예외로, 예외 처리를 하지 않아도 컴파일 오류가 발생하지 않습니다. Checked Exception
)RuntimeException
을 제외한 Exception
클래스의 모든 하위 클래스는 체크 예외입니다. 컴파일러는 이러한 예외가 발생할 가능성이 있는 메서드에 대해 예외 처리를 강제합니다.try-catch
블록)로 처리하거나, 메서드 시그니처에 throws
키워드를 사용해 상위 메서드로 예외를 전달해야 합니다. try-catch
블록: 예외 발생 가능성이 있는 코드를 try
블록 내에 작성하고, 발생한 예외를 catch
블록에서 처리합니다. catch
블록을 사용할 수 있습니다.try {
// 예외 발생 가능 코드
} catch (IOException e) {
// IOException 처리
} catch (SQLException e) {
// SQLException 처리
}
finally
블록: try
또는 catch
블록이 완료된 후 반드시 실행되는 블록입니다. try {
// 예외 발생 가능 코드
} catch (Exception e) {
// 예외 처리
} finally {
// 리소스 해제 또는 정리 작업
}
throw
키워드를 사용합니다.throw
키워드를 이용하면 오류를 발생시킬 수 있습니다. (주로 조건문과 함께 쓰입니다.)throws
키워드를 사용해 명시해야 합니다.public void readFile(String filePath) throws IOException {
if (filePath == null) {
throw new IOException("파일 경로가 null입니다.");
}
// 파일 읽기 작업
}
catch
블록에서 예외를 처리한 후, 같은 예외 또는 새로운 예외를 다시 던질 수 있습니다. try {
// 예외 발생 가능 코드
} catch (IOException e) {
// 예외 로깅 등 추가 작업
throw e; // 동일한 예외 다시 던지기
}
Exception
또는 RuntimeException
을 상속하여 구현합니다.public class InvalidUserInputException extends Exception {
public InvalidUserInputException(String message) {
super(message);
}
}
import java.io.*;
public class FileManager {
// 파일을 읽는 메서드
public void readFile(String filePath) throws IOException {
File file = new File(filePath);
// 파일이 존재하지 않으면 IOException 발생
if (!file.exists()) {
throw new IOException("파일이 존재하지 않습니다: " + filePath);
}
// 파일 읽기 작업
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
}
public static void main(String[] args) {
FileManager fileManager = new FileManager();
try {
// 체크 예외 처리: IOException을 처리하기 위해 try-catch 사용
fileManager.readFile("example.txt");
} catch (IOException e) {
// 예외 처리: 오류 메시지 출력
System.err.println("파일을 읽는 동안 오류가 발생했습니다: " + e.getMessage());
}
}
}
FileManager
클래스에서 파일을 읽는 readFile
메서드를 구현한 것입니다. IOException
이라는 체크 예외를 던질 수 있습니다. throws
키워드: readFile
메서드는 throws
키워드를 사용해 IOException
을 호출자에게 넘깁니다. main
메서드에서 readFile
을 호출할 때, try-catch
블록을 사용해 IOException
을 처리합니다. catch
블록에서는 오류 메시지를 출력하여 예외를 처리합니다.throws
를 통한 예외 전파throws
키워드를 사용하여 발생할 수 있는 예외를 호출자에게 넘겨줍니다. 이를 통해 예외 처리를 메서드를 호출하는 쪽에서 처리할 수 있게 합니다.public void readFile(String filePath) throws IOException
try-catch
블록을 통한 예외 처리try-catch
블록을 사용해 처리합니다. try { /* 예외 발생 가능 코드 */ } catch (IOException e) { /* 예외 처리 */ }
RuntimeException
클래스를 상속하는 모든 예외를 포함합니다. RuntimeException
은 Exception
클래스의 서브클래스이므로, 언체크 예외도 Throwable
계층 구조의 일부입니다.public class UncheckedExceptionExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
try {
// ArrayIndexOutOfBoundsException 발생 가능
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
// 예외 처리
System.err.println("배열 인덱스가 잘못되었습니다: " + e.getMessage());
}
// NullPointerException 발생 가능
String text = null;
try {
System.out.println(text.length());
} catch (NullPointerException e) {
// 예외 처리
System.err.println("널 포인터 예외 발생: " + e.getMessage());
}
}
}
ArrayIndexOutOfBoundsException
과 NullPointerException
이라는 두 가지 언체크 예외를 보여줍니다. RuntimeException
의 하위 클래스이며, 발생 시점에 예외를 처리하지 않아도 컴파일러가 이를 강제하지 않습니다.ArrayIndexOutOfBoundsException
: 배열의 잘못된 인덱스에 접근하려 할 때 발생합니다. 예제에서는 배열의 길이를 초과하는 인덱스에 접근하려고 시도하여 예외가 발생합니다.NullPointerException
: 객체가 null일 때 해당 객체의 메서드를 호출하려 할 때 발생합니다. 예제에서는 null인 문자열 변수에 대해 length()
메서드를 호출하려고 시도하여 예외가 발생합니다.try-catch
블록을 사용할 수도 있습니다.finally
블록과 try-with-resources
구문을 제공합니다.finally
블록은 예외가 발생하더라도 반드시 실행되는 블록입니다. try-catch
구문과 함께 사용되며, 주로 리소스를 해제하거나 중요한 정리 작업을 수행하는 데 사용됩니다. finally
블록은 항상 실행됩니다.import java.io.*;
public class FileManager {
public void readFile(String filePath) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filePath));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("파일을 읽는 동안 오류가 발생했습니다: " + e.getMessage());
} finally {
// 리소스 해제
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.err.println("리소스를 해제하는 동안 오류가 발생했습니다: " + e.getMessage());
}
}
}
}
public static void main(String[] args) {
FileManager fileManager = new FileManager();
fileManager.readFile("example.txt");
}
}
try
블록: 리소스를 사용해 파일을 읽습니다.catch
블록: 파일 읽기 중 발생한 예외를 처리합니다.finally
블록: 파일을 읽은 후 BufferedReader
리소스를 닫아야 합니다. 이 블록은 예외 발생 여부와 상관없이 항상 실행되므로, 리소스 해제에 적합합니다.finally
블록을 사용할 때는 개발자가 리소스를 직접 관리해야 하며, 코드가 복잡해질 수 있습니다.try-with-resources
구문은 자바 7에서 도입된 기능으로, AutoCloseable
인터페이스를 구현한 리소스(예: 파일, 소켓, 데이터베이스 연결)를 자동으로 닫아주는 기능을 제공합니다. finally
블록을 사용하지 않고도 리소스를 자동으로 해제할 수 있어 코드가 더 간결하고 안전해집니다.AutoCloseable
인터페이스는 java.lang
에 포함되어 있는 인터페이스이기 때문에, 별도의 import
구문 없이 사용하실 수 있습니다.import java.io.*;
public class FileManager {
public void readFile(String filePath) {
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("파일을 읽는 동안 오류가 발생했습니다: " + e.getMessage());
}
// try-with-resources 구문은 자동으로 리소스를 해제합니다.
}
public static void main(String[] args) {
FileManager fileManager = new FileManager();
fileManager.readFile("example.txt");
}
}
try-with-resources
구문: try
블록에 선언된 리소스는 블록이 종료되면 자동으로 닫힙니다. BufferedReader
가 자동으로 닫히므로, finally
블록을 사용할 필요가 없습니다.try-with-resources
구문을 사용하려면 리소스가 AutoCloseable
인터페이스를 구현(implements)해야 합니다. try
구문 바로 뒤에 괄호()
를 통해 try-with-resources
구문을 이용할 수 있습니다.// CustomResource.java
public class CustomResource implements AutoCloseable {
public CustomResource() {
System.out.println("CustomResource가 열렸습니다.");
}
public void doSomething() {
System.out.println("CustomResource에서 작업을 수행 중입니다.");
}
@Override
public void close() {
System.out.println("CustomResource가 닫혔습니다.");
}
}
// Main.java
public class Main {
public static void main(String[] args) {
// try-with-resources 구문에서 커스텀 리소스 사용
try (CustomResource resource = new CustomResource()) {
resource.doSomething();
} catch (Exception e) {
System.err.println("예외 발생: " + e.getMessage());
}
}
}
CustomResource가 열렸습니다.
CustomResource에서 작업을 수행 중입니다.
CustomResource가 닫혔습니다.
CustomResource
클래스: 이 클래스는 AutoCloseable
인터페이스를 구현하여 close()
메서드를 제공합니다. 이 메서드는 리소스를 닫을 때 자동으로 호출됩니다.doSomething()
메서드: CustomResource
클래스의 메서드로, 리소스를 사용하여 어떤 작업을 수행하는 역할을 합니다.try-with-resources
구문: CustomResource
객체를 try
블록 내에서 생성하고 사용합니다. 블록이 종료되면 자동으로 close()
메서드가 호출되어 리소스가 안전하게 닫힙니다.Unchecked Exception
)와 예외 계층
을 적절히 활용하는 방향으로 예외 처리 전략이 진화해왔습니다.RuntimeException
을 상속하는 예외로, 컴파일러가 예외 처리를 강제하지 않습니다. throws
선언을 추가해야 하는 불편함이 있습니다.DatabaseException
이라는 부모 클래스를 만들고, 이를 상속하는 ConnectionException
, QueryException
등을 정의할 수 있습니다. InsufficientFundsException
과 같은 커스텀 예외를 정의하여, 특정 비즈니스 로직에서 발생하는 예외를 처리할 수 있습니다.RuntimeException
과 Exception
같은 상위 클래스를 사용하여 다양한 예외를 포괄적으로 처리하는 방법을 보여줍니다. RuntimeException
은 언체크 예외를, Exception
은 체크 예외와 언체크 예외를 모두 처리할 수 있습니다.public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
processInput(null); // NullPointerException 발생 가능
divideNumbers(10, 0); // ArithmeticException 발생 가능
accessArrayElement(new int[]{1, 2, 3}, 5); // ArrayIndexOutOfBoundsException 발생 가능
simulateCheckedException(); // IOException 발생 가능 (체크 예외)
} catch (RuntimeException e) {
// RuntimeException과 그 하위 예외를 포괄적으로 처리
System.err.println("런타임 예외 발생: " + e.getClass().getSimpleName() + " - " + e.getMessage());
} catch (Exception e) {
// Exception과 그 하위 예외를 포괄적으로 처리
System.err.println("예외 발생: " + e.getClass().getSimpleName() + " - " + e.getMessage());
}
}
public static void processInput(String input) {
if (input == null) {
throw new NullPointerException("입력 값이 null입니다.");
}
}
public static int divideNumbers(int a, int b) {
return a / b; // 0으로 나누면 ArithmeticException 발생
}
public static int accessArrayElement(int[] array, int index) {
return array[index]; // 잘못된 인덱스 접근 시 ArrayIndexOutOfBoundsException 발생
}
public static void simulateCheckedException() throws Exception {
throw new Exception("체크 예외 발생!");
}
}
런타임 예외 발생: NullPointerException - 입력 값이 null입니다.
processInput(null)
에서 NullPointerException
이 발생하여 RuntimeException
블록에서 처리됩니다. try
블록processInput(null)
은 NullPointerException
을 발생시킬 수 있습니다.divideNumbers(10, 0)
은 ArithmeticException
을 발생시킬 수 있습니다.accessArrayElement(new int[]{1, 2, 3}, 5)
는 ArrayIndexOutOfBoundsException
을 발생시킬 수 있습니다.simulateCheckedException()
은 Exception
을 던질 수 있습니다.catch
블록RuntimeException e
: 모든 언체크 예외(RuntimeException
과 그 하위 클래스)를 처리합니다. NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException
을 포괄적으로 처리할 수 있습니다.Exception e
: RuntimeException
으로 처리되지 않은 모든 예외를 처리합니다. Exception
을 직접 던지는 체크 예외(simulateCheckedException
에서 발생)와 모든 체크 예외를 처리할 수 있습니다.예외 계층: 자바의 예외는 Throwable
가 최상위 클래스이며, Error
와 Exception
으로 나뉩니다. Error
는 복구 불가능한 시스템 오류를, Exception
은 프로그램에서 처리 가능한 예외를 나타냅니다.
체크 예외: 컴파일러가 예외 처리를 강제하는 예외로, 파일 I/O나 네트워크 연결 등 외부 자원에 의존하는 작업에서 주로 발생합니다. throws
키워드를 통해 상위 메서드로 전파하거나, try-catch
블록으로 처리해야 합니다.
언체크 예외: RuntimeException
을 상속하는 예외로, 컴파일러가 예외 처리를 강제하지 않습니다. 주로 프로그래머의 실수로 발생하며, 필요에 따라 예외 처리를 하거나 방어적인 코드를 작성해 예방할 수 있습니다.
리소스 반환: 리소스 누수를 방지하기 위해 finally
블록이나 try-with-resources
구문을 사용해 리소스를 안전하게 반환해야 합니다. try-with-resources
는 자바 7 이후 도입된 기능으로, AutoCloseable
을 구현한 리소스를 자동으로 닫아줍니다.
실무에서의 예외 처리: 실무에서는 체크 예외와 언체크 예외를 상황에 맞게 조합해 사용하며, 필요에 따라 커스텀 예외를 정의해 예외 계층을 설계합니다. 예외를 통해 프로그램 흐름을 제어하는 것은 피해야 하며, 예외는 예외적인 상황에서만 사용해야 합니다.