• 자바 라이브러리에는 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)가 화면에 출력해준다.