• 자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다.
◦ InputStream, OutputStream 등의 IO 라이브러리
◦ 터미널 등에서 명령어를 이용해 메모장을 열고, close 하지 않은 상태에서 메모장 파일을 마우스 클릭으로 실행해보면
위와 같은 에러메세지를 확인할 수 있다.
◦ java.sql.Connection 등의 JDBC 사용을 위한 라이브러리
• 어떠한 이유(오류 또는 개발자의 실수)에 의해서 close가 안되면, 프로그램에 문제가 생길 수 있다.
• 이런 자원의 상당수가 안전망으로 finalizer를 활용하고 있긴 하나, 그리 믿을만하지는 못하다.
자원의 닫힘을 보장해주는 수단 : try-finally
• try와 finally는 짝꿍으로 붙어다녀야 하며, try블록에서 예외가 나던/나지않던 finally 안의 구문은 실행된다.
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try { return br.readLine(); }
finally { br.close(); }
} // read 만 하고 close 하는 경우
static void copy(String src, String dst) throws IOException {
InputStream in = new FileInputStream(src);
try { OutputStream out = new FileOutputStream(dst);
try {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0) out.write(buf, 0, n);
} finally { out.close(); }
} finally { in.close(); }
} // read 도 하고 write 도 하고싶은 경우 : close 를 2번 해줘야 한다.
• 코드의 가독성이 너무 나쁘다. 자원이 100개가 되면 try-finally가 100개가 생긴다.
static String firstLineOfFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try { return br.**readLine**(); }
finally { br.**close**(); }
}
• 특히 예외는 try 블록과 finally 블록 모두에서 발생할 수 있는데, 위 예시에서 기기에 문제가 생기는 경우
◦ readLine 메서드가 먼저 예외를 던진다 : 파일을 읽을 수 없습니다.
◦ 그 다음 close 메서드가 예외를 던진다 : 파일을 close 할 수 없습니다.
→ 이런 상황이 발생하면 스택 추적 내역에 첫번째 예외에 관한 정보는 남지 않는다 : 디버깅이 어려워짐
해결책의 등장 : try-with-resources
• try 안에 파라미터로 자원을 넘겨주는 방식
• try 문에서 선언된 객체들에 대해서 try가 종료될 때 자동으로 자원을 해제해주는 기능이 있다.
• 먼저 이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다.
◦ AutoCloseable 인터페이스 : void 타입의 close 메서드 하나만 덩그러니 정의된 인터페이스
public interface AutoCloseable { void close() throws Exception; }
◦ public interface AutoCloseable { void close() throws Exception; }
◦ try-with-resources가 모든 객체의 close를 호출해주지는 않음
◦ AutoCloseable을 구현한 객체만 close가 호출된다.
public abstract class BufferedReader implements AutoCloseable { ... }
static String firstLineOfFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
static void copy(String src, String dst) throws IOException {
try (Input in = new FileInput(src); Output out = new FileOutput(dst)) {
byte[] buf = new byte[BUFFER_SIZE];
int n;
while ((n = in.read(buf)) >= 0) out.write(buf, 0, n);
}
}
• 코드의 가독성이 훨씬 좋아지고 문제 진단에도 훨씬 유리하다.
• readLine 과 close 에서 모두 예외가 발생하면 먼저 발생한 readLine의 예외가 기록되고, close에서 발생한 예외는 숨겨지기 때문
• 이렇게 숨겨진 예외들은 스택 추적 내역에 숨겨졌다(suppressed)는 꼬리표를 달고 출력된다.
◦ Throwable의 getSuppressed 메서드를 이용하면 프로그램 코드에서 가져올 수도 있다.
void addSuppressed(Throwwable exception)
public final Throwable[] getSuppressed()
◦ 숨겨지는것 보단, 더 자세한 출력이 진짜 목적
try-with-resources 와 함께 쓰이는 catch 구문
• try-with-resources와 catch 절을 함께 쓰면 try 문을 중첩하지 않고도 다수의 예외를 처리할 수 있다.
try {
// 프로그램에서 사용하는 일반적인 코드를 입력
// 코드 실행 중 에러가 나면 그 자리에서 중단되고 catch문으로 이동
// 오류가 없다면 try 안의 구문을 모두 실행한다.
} catch(Exception e) {
// try에서 오류가 나면 catch안의 내용을 실행
// try에 오류가 없다면 catch는 실행되지 않는다.
}
static String firstLineOfFile (String path, String defaultVal){
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (IOException e) { return defaultVal; }
} // 파일을 여는 것에 실패하거나, 데이터를 읽지 못했을 때 예외 대신 기본값을 반환하도록 수정된 코드
• 예외처리란, 프로그래머가 예기치못한 예외의 발생에 미리 대처하는 코드를 작성하는 것
◦ (나쁜)사용자가 발생시키는 예외에 대해, 개발자가 미리 대처를 해줄 수 있다.
◦ 실행중인 쓰레드의 비정상적인 종료를 막고 상태를 정상상태로 유지하는 것이 목적
• 예외가 처리되지 못한경우, 쓰레드은 비정상적으로 종료되며 처리되지 못한 예외의 원인을 JVM의 예외처리기
(UncaughtExceptionHandler)가 화면에 출력해준다.
'Java' 카테고리의 다른 글
[Effective Java] ITEM19 : 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 (0) | 2022.07.30 |
---|---|
[Effective Java] ITEM14 : Comparable을 구현할지 고려하라 (0) | 2022.07.30 |
[Effective Java] ITEM7 : 다 쓴 객체 참조를 해제하라 (0) | 2022.07.24 |
[Effective Java] ITEM5 : 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.07.24 |
[Effective Java] ITEM3 : private 생성자 혹은 enum 타입으로 싱글톤임을 보증하라 (0) | 2022.07.23 |