자바 예외 총정리
예외란?
사용자의 잘못된 조작 혹은 개발자의 로직 실수로 인해서 발생하는 프로그램 오류를 말한다. 예외처리 코드를 통해서 프로그램을 종료하지 않고 다시 정살 실행 상태가 유지 되도록 할 수 있다.
예외 vs 에러
에러(error)란 자바 프로그램 밖에서 발생한 오류를 뜻한다. 컴퓨터가 고장이 난다거나. OS에서 비정상적인 상황이 발생해서 JVM이 실행될 수 없는 경우등으로 복구가 불가능 하다.
- error : 프로세스에 영향을 줌
- exception : 스레드에만 영향을 줌
예외 처리 방법
- 예외 복구 : 상황 파악 후 문제 해결해서 정상 상태로 돌려놓는 방법
- 예외 회피 : throws를 통해서 직접 처리 안하고 호출한 쪽으로 던져 회피
- 예외 전환 : 회피와 비슷하지만 그냥 던지지 않고 적합한 의미를 가진 예외로 변경해서 던지는 것
예외 처리에는 throws, try-catch문을 많이 사용하는데 어떤 차이가 있는지 한번 알아보자
Try-catch VS Throw
1. try-catch
try {
// 예외가 발생할 수 있는 코드
} catch (ExceptionType1 e1) {
// ExceptionType1 예외를 처리하는 코드
} catch (ExceptionType2 e2) {
// ExceptionType2 예외를 처리하는 코드
} finally {
// 예외 발생 여부와 상관없이 항상 실행되는 코드 (선택 사항)
}
try-catch는 "예외를 메서드 안에서 직접 처리한다." 라는 개념만 알고있으면된다.
사용자는 예외가 발생할 수 있는 코드가 어떤 예외를 발생할 수 있는지 미리 식별하고
catch에 발생할수 있는 예외타입을 적어두고 그 예외가 발생하면 어떤 행동을 할지 적어두는 것이다.
finally를 통해서 예외 발생 여부와 상관없이 실행되는 코드를 적을 수도 있다.
이런 방식은 4가지 장점이 있다.
- 예외 처리의 일관성:
코드안의 예외를 잡아서 처리함으로써 예측 블가능한 종료를 방지한다. - 코드 흐름 제어:
예외 발생시 적절한 조치를 바로 취할 수 있다. - 디버깅 용이:
예외가 발생한 부분을 정확히 확인할 수 있고 문제를 진단할 수 있다. - 자원 해제:
finally 블록을 사용하여 파일, 네트워크 연결 등과 같은 자원을 확실히 해제할 수 있다. 예외 발생 여부와 상관없이 자원 해제를 보장한다.
2. throws
import java.io.*;
import java.sql.*;
public class MultiThrowsExample {
public static void main(String[] args) {
try {
performDatabaseOperation();
} catch (SQLException | IOException e) {
System.out.println("예외 처리: " + e.getMessage());
}
}
public static void performDatabaseOperation() throws SQLException, IOException {
// 데이터베이스 작업 중 발생할 수 있는 예외
if (Math.random() > 0.5) {
throw new SQLException("데이터베이스 오류 발생");
} else {
throw new IOException("입출력 오류 발생");
}
}
}
throws는 "호출자에게 예외를 위인임한다." 라는 개념만 알고있으면된다.
예외가 발생했을 때 메서드 안에서 해결하지 않고 메서드를 호출한 호출자에게 위임하는 것이다.
위 예시를 보자. performDatabaseOperation메소드에서는 데이터베이스 혹은 입출력 오류가 발생할 수 있다.
이를 메서드 안에서 처리하지 않고 호출자인 main에서 처리하도록 한다.
이런 방식은 장점이 3가지가 있다.
- 예외 처리의 일관성
예외 처리를 호출자에서 처리함으로써 코드의 일관성을 유지할 수 있다. - 책임 분리
메서드의 주 기능과 예외 처리를 분리해 코드의 가독성, 유지보수성을 높일 수 있다. - 단순화:
복잡한 예외처리를 피하고 호출자에게 더 많은 제어권을 부여한다.
예외 처리는 최대한 커스텀해서 자세하게 작성하는 것이 좋으나 어떤 예외가 있는지 기본은 알아야 하지 않는가?
그럼이제 계층별 예외를 한번 확인해보자
1. Checked Exceptions
자바 컴파일시에 체크되는 예외이다. 프로그램이 실행되기 전에 컴파일러에 의해서 반드시 예외 처리가 필요하다고 판단하게되어 예외처리를 하지 않으면 코드를 실행시킬 수 없다. 주로 외부 환경과의 상호작용이다.
1-1. IOException
입출력 작업 중 발생하는 예
import java.io.*;
public class IOExceptionExample {
public static void main(String[] args) {
try {
FileReader file = new FileReader("nonexistentfile.txt");
} catch (IOException e) {
System.out.println("IOException 발생: " + e.getMessage());
}
}
}
1-1-1. FileNotFoundException
파일이 존재하지 않을 때 발생
import java.io.*;
public class FileNotFoundExceptionExample {
public static void main(String[] args) {
try {
FileInputStream file = new FileInputStream("nonexistentfile.txt");
} catch (FileNotFoundException e) {
System.out.println("FileNotFoundException 발생: " + e.getMessage());
}
}
}
1-1-2. EOFException
입력 스트림의 끝에 도달했을 때 발생 (더이상 읽을 것이 없는 데 읽어야 할때)
import java.io.*;
public class EOFExceptionExample {
public static void main(String[] args) {
try {
DataInputStream input = new DataInputStream(new FileInputStream("file.txt"));
while (true) {
input.readUTF();
}
} catch (EOFException e) {
System.out.println("EOFException 발생: " + e.getMessage());
} catch (IOException e) {
System.out.println("IOException 발생: " + e.getMessage());
}
}
}
1-2. SQLException
데이터 베이스 작업 중 발생하는 예외
import java.sql.*;
public class SQLExceptionExample {
public static void main(String[] args) {
try {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "user", "password");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM nonexistenttable");
} catch (SQLException e) {
System.out.println("SQLException 발생: " + e.getMessage());
}
}
}
이런식으로 직접적으로 연결하는 방법은 잘 안쓰니 잘 볼일이 없다.
1-3. ClassNotFoundException
클래스 파일을 찾을 수 없을 때 발생
public class ClassNotFoundExceptionExample {
public static void main(String[] args) {
try {
Class.forName("com.nonexistent.ClassName");
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFoundException 발생: " + e.getMessage());
}
}
}
1-4. InterruptedException
스레드가 중단되었을 때 발생
public class InterruptedExceptionExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("InterruptedException 발생: " + e.getMessage());
}
});
thread.start();
thread.interrupt();
}
}
1-5. NoSuchMethodExceptionExample
특정 메서드를 찾을 수 없을 때 발생
import java.lang.reflect.*;
public class NoSuchMethodExceptionExample {
public static void main(String[] args) {
try {
Method method = String.class.getMethod("nonexistentMethod");
} catch (NoSuchMethodException e) {
System.out.println("NoSuchMethodException 발생: " + e.getMessage());
}
}
}
2. Checked Exceptions
컴파일러가 확인하지 않아서 예외처리를 반드시 할필요는 없으며 주로 프로그래밍 오류를 나타낸다.
2-1. NullPointerException
null 객체를 참조할 때 발생
public class NullPointerExceptionExample {
public static void main(String[] args) {
String str = null;
try {
str.length();
} catch (NullPointerException e) {
System.out.println("NullPointerException 발생: " + e.getMessage());
}
}
}
2-2. ArrayIndexOutOfBoundsException
배열 인덱스가 범위를 벗어났을 때 발생
public class ArrayIndexOutOfBoundsExceptionExample {
public static void main(String[] args) {
int[] array = new int[5];
try {
int number = array[10];
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException 발생: " + e.getMessage());
}
}
}
2-3. ArithmeticException
산술 연산 오루가 발생할 때 (ex: 0 나누기)
public class ArithmeticExceptionExample {
public static void main(String[] args) {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("ArithmeticException 발생: " + e.getMessage());
}
}
}
2-4-1. IllegalArgumentException
잘못된 인자 전달
public class IllegalArgumentExceptionExample {
public static void main(String[] args) {
try {
Thread.sleep(-1000);
} catch (IllegalArgumentException e) {
System.out.println("IllegalArgumentException 발생: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("InterruptedException 발생: " + e.getMessage());
}
}
}
2-4-2. IllegalArgumentException
문자열을 숫자로 변환할 때, 형식이 잘못되었을 때
public class NumberFormatExceptionExample {
public static void main(String[] args) {
String str = "abc";
try {
int number = Integer.parseInt(str);
} catch (NumberFormatException e) {
System.out.println("NumberFormatException 발생: " + e.getMessage());
}
}
}
3. 사용자 정의 예외
이제 표준 예외 클래스에 어떤 것이 있는지 알았으니 상황에 맞는 예외를 정의하는 법에 대해서 알아보자
사용자 정의는 왜 사용하나요?
조금더 구체적인 예외를 사용함으로써 복잡한 조건문을 줄인다거나 좀더 체계적인 관리가 가능해 진다.
1. 사용자 정의 예외 클래스 작성법
public class MyCustomException extends Exception {
// 기본 생성자
public MyCustomException() {
super();
}
// 예외 메시지를 받는 생성자
public MyCustomException(String message) {
super(message);
}
// 예외 메시지와 원인(cause)을 받는 생성자
public MyCustomException(String message, Throwable cause) {
super(message, cause);
}
// 원인(cause)을 받는 생성자
public MyCustomException(Throwable cause) {
super(cause);
}
}
클래스 작성법은 크게 생성자로 나뉜다.
상황에 맞는 생성자를 사용하면 된다.
2. 사용자 정의 예외 발생 시키기
public class MyService {
public void performAction(int value) throws MyCustomException {
if (value < 0) {
throw new MyCustomException("Value cannot be negative: " + value);
}
// 비즈니스 로직 처리
}
}
throw를 사용해서 특정 조건에 만족하면 사용자 정의 예외를 던지도록 한다.
3. 예외 호출자가 처리하기
public class Main {
public static void main(String[] args) {
MyService service = new MyService();
try {
service.performAction(-1);
} catch (MyCustomException e) {
System.out.println("예외 처리: " + e.getMessage());
}
}
}
사용자 정의 예외를 받은 호출자는 예외 상황에 따른 처리를 하도록한다.
로깅을 한다거나 상황을 처리한다거나 할 수 있다.
예외상황에 유연하게 대처하는 개발자가 되보로록 하자
'JAVA > JAVA 기초' 카테고리의 다른 글
[Java/기초] 접근 제한자 (public, protected, default, private) (0) | 2024.04.12 |
---|---|
[Java/기초] abstract (0) | 2024.03.04 |
[Java/기초] Overloading, Overriding (0) | 2024.03.04 |
[Java/기초] Static, Final (0) | 2024.03.04 |
[Java/기초] Getter Setter (0) | 2024.02.13 |
Coding, Software, Computer Science 내가 공부한 것들 잘 이해했는지, 설명할 수 있는지 적는 공간