자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
자바가 제공하는 예외 계층 구조
Exception과 Error의 차이는?
RuntimeException과 RE가 아닌 것의 차이는?
커스텀한 예외 만드는 방법
자바에서 예외 처리 방법 (try, catch, throw, throws, finally)
자바의 예외처리
프로그램 실행 중에 발생할 수 있는 예상치 못한 상황을 나타내는 객체
이러한 예외상황을 적절히 처리하고 프로그램의 안정성을 높이기 위해 중요함
try {
// 예외가 발생할 수 있는 코드
} catch (ExceptionType1 e1) {
// 예외 처리 코드
} catch (ExceptionType2 e2) {
// 예외 처리 코드
} finally {
// 선택적인 finally 블록 (필요한 경우 사용)
}
try 문 = 예외가 발생할 수 있는 코드를 작성
catch 문 = 해당 예외를 처리하는 코드를 작성
finally 문 = 선택적으로 작성하나 주로 자원의 반납에 사용
public void throwException() throws Exception {
throw new Exception("error");
}
throw
예외를 명시적으로 발생시킬 때 사용
예외가 발생하는 상황을 감지하고 예외 객체를 생성하여 해당 예외를 처리할 코드 블록으로 전달
throw 키워드가 try-catch 문 내부에서 사용되면 예외 발생 시 catch문으로 제어를 이동시킴
throws
메서드 선언부에 사용되며 메서드가 호출될 때 발생할 수 있는 예외를 명시적으로 선언함
메서드에서 발생할 수 있는 예외를 호출자에게 알려줌으로써 예외 처리를 해당 메서드를 호출한 곳에서 처리할 수 있도록 함
메서드 시그니처 뒤에 예외 클래스들을 선언하고 여러 개의 예외를 선언할 경우 쉼표로 구분
throws로 선언된 예외는 직접 try-catch 문을 사용하여 처리하거나 상위 호출자에게 예외를 전달할 수 있음
FileReader reader = null;
try {
reader = new FileReader("file.txt");
// 파일 읽기 작업 수행
// 예외가 발생할 수 있는 코드
} catch (IOException e) {
// 예외 처리 코드
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// 자원 해제 실패에 대한 예외 처리
}
}
}
try (FileReader reader = new FileReader("file.txt")) {
// 파일 읽기 작업 수행
System.out.println("do");
// 예외가 발생할 수 있는 코드
} catch (IOException e) {
// 예외 처리 코드
} finally {
// finally 구문도 추가적으로 사용 가능 -> try with resources는 finally 블록에서 close 로직을 생성하는게 아니라 catch 블록에서 close하는 로직을 생성해주고 또 맨 밑에 close하는 로직을 추가로 생성해줌
}
// try-with-resources를 컴파일한 class파일
try{
FileReader reader = new FileReader("file.txt");
try {
System.out.println("do");
} catch (Throwable var5) {
try {
reader.close();
} catch (Throwable var4) {
var5.addSuppressed(var4);
}
throw var5;
}
reader.close();
} finally {
//finally 추가시 try로 한번더 감싸져서 finally가 추가됨
}
예외처리 메커니즘을 활용하여 자원관리를 간편하게 해주는 기능(finally 블록을 줄여줌)
try 안에 생성하는 자원은 AutoCloseable 인터페이스를 구현한 클래스이어야만 함
AutoCloseable의 close메서드를 명시적으로 호출하므로 자동으로 자원을 정리함
코드의 가독성과 안정성을 향상하고, 자원 누수를 방지함
public class JavaPuzzler40 {
static void copy(String src, String dest) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int n;
while((n = in.read(buf)) >= 0){
out.write(buf, 0, n);
}
} finally {
if(in != null) {
in.close(); // in.close하는 부분도 try catch로 잡아주어야 다음 out.close하는 부분으로 내려가 자원을 종료할 수 있음, 만약 에러가 던져지면 out의 자원은 종료되지 못하고 자원 누수가 발생됨
}
if(out != null) {
out.close();
}
}
}
}
finally 안에 return문(안티패턴)
public class FinallyReturnTest {
public static void main(String[] args) {
System.out.println(new FinallyReturnTest().returnTest()); // 무조건 finally에서 리턴된 값이 나옴
}
public String returnTest() {
try {
System.out.println("try");
// throw new IllegalStateException("error"); // 에러발생시에는 catch 값까지 print한 후 finally 값이 리턴됨
return "try";
} catch (Exception e) {
System.out.println("catch");
return "catch";
} finally {
System.out.println("finally");
// throw new IllegalStateException("error"); // 이런식으로도 사용 x
return "finally"; // 이런식으로도 사용 x
}
}
}
try 문 return = finally 블록을 거쳐 정상 실행
catch 문 return = finally 블록을 거쳐 정상 실행
finally 문 return = try 블록 안에서 발생한 예외 무시되고 finally 거쳐 정상 종료(예외를 알 수 없음)
finally에서는 코드의 흐름을 제어하는 로직은 작성하지 않는 게 좋음
finally에서는 자원 반납 혹은 로깅정도만 하는 형식으로 사용하는 것을 권장함
자바가 제공하는 예외 계층 구조
java.lang.Throwable
모든 예외 클래스의 최상위 클래스
예외와 에러의 기본 클래스 = Exception과 Error 클래스의 부모 클래스
java.lang.Exception
일반적인 예외 상황을 나타내는 클래스
다양한 예외 하위 클래스를 가짐
Exception 클래스를 상속받은 예외들은 예외처리가 필요한 Checked Exception임