관리 메뉴

The Nirsa Way

[Effective Java 3/E - 객체 생성과 파괴] Item 9. try-finally 보다는 try-with-resources를 사용하라 본문

Development/JAVA

[Effective Java 3/E - 객체 생성과 파괴] Item 9. try-finally 보다는 try-with-resources를 사용하라

KoreaNirsa 2025. 7. 13. 14:40
반응형

 

try-finally 보다는 try-with-resources를 사용하라

try-finally의 경우 인스턴스를 명시적으로 close()를 호출하여 닫아주어야 합니다. 만약 개발자의 실수로 자원을 제대로 닫지 않는다면 성능의 문제가 발생할 가능성이 있으며 try와 finally 모두 예외가 발생된다면 try에서 발생한 예외가 덮어씌워질 수 있습니다.

file.txt의 첫번째 줄을 읽은 후 출력하는 코드를 try-finally를 사용하여 구현한다고 하면 아래와 같이 될겁니다.

finally를 주의 깊게 본다면 reader가 null이 아닌지 체크(NPE 방지)를 하고 close()를 수행합니다. 하지만 close()  수행 중 IOException이 발생할 가능성이 있으므로 또 다시 try-catch를 사용하여 예외를 처리해주어야 합니다.

파일을 읽고 출력하는 아주 간단한 코드이지만 길고 복잡해지며 NPE 방지, close() 누락 등 개발자의 실수가 발생할 수 있으며, 예외 발생 시 디버깅이 어려워 집니다. try와 close() 양쪽의 예외에서 한쪽의 예외만 보일 수 있기 때문입니다. 이러한 문제들로 인해 try-with-resources 방식을 권장합니다.

public class Test {
    public static void main(String[] args) throws InterruptedException {
    	BufferedReader reader = null;
    	try {
    	    reader = new BufferedReader(new FileReader("file.txt"));
    	    String line = reader.readLine();
    	    System.out.println(line);
    	} catch (IOException e) {
    	    e.printStackTrace();
    	} finally {
    	    if (reader != null) {
    	        try {
    	            reader.close();
    	        } catch (IOException e) {
    	            e.printStackTrace();
    	        }
    	    }
    	}
    }
}

 

위와 똑같은 기능을 구현하는 코드이지만 코드가 훨씬 더 간단해졌으며, 개발자의 실수를 유도할 코드가 사라졌습니다. (NPE 방지, close 등) try-with-resources는 자동으로 close()를 호출해주므로 개발자가 따로 작성할 필요가 없습니다.

public class Test {
    public static void main(String[] args) throws InterruptedException {
    	try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    	    String line = reader.readLine();
    	    System.out.println(line);
    	} catch (IOException e) {
    	    e.printStackTrace();
    	}
    }
}

 

만약 여러 리소스를 사용해야 한다면 아래이 try에 여러 리소스를 생성하여 사용할 수 있습니다.

public class Test {
    public static void main(String[] args) throws InterruptedException {
        try (FileInputStream fis = new FileInputStream("input.txt");
                FileOutputStream fos = new FileOutputStream("output.txt");
                BufferedInputStream bis = new BufferedInputStream(fis);
                BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            int data;
            while ((data = bis.read()) != -1) {
                bos.write(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 


 

try-with-resources를 사용하기 위해서는 AutoCloseable을 상속받아라.

단, try-with-resources의 경우 AutoCloseable를 상속받아야만 사용이 가능합니다. AutoCloseable은 try-with-resources와 함께 사용할 수 있도록 설계되었으며 이름처럼 자동으로 close()를 호출되도록 보장해줍니다. 많은 Java의 클래스들이 AutoClosable 인터페이스를 구현하며 LIFO(후입선출) 방식으로 닫힙니다.

만약, AutoCloseable을 상속받지 않은 상태로 try-with-resources를 사용하려 한다면 "The resource type MyResource does not implement java.lang.AutoCloseable" 컴파일 에러가 발생합니다.

 

직접 만든 클래스에 AutoCloseable을 적용하려면 상속받은 후 close() 메서드를 오버라이딩 해주면 됩니다.

public class Test {
    public static void main(String[] args) throws InterruptedException {
        try (MyResource res = new MyResource()) {
        	res.doSomething();
        } catch (Exception e) {
        	e.printStackTrace();
        }
    }
}

// AutoCloseable 상속
class MyResource implements AutoCloseable {
    public void doSomething() {
    	System.out.println("리소스 사용 중...");
    }

    // close() 오버라이딩
    @Override
    public void close() {
    	System.out.println("리소스 정리 중(close 호출됨)");
    }
}

 

만약, 강제로 예외를 발생시켜도 close는 정상적으로 호출되어 리소스가 닫힙니다.

public class Test {
    public static void main(String[] args) throws InterruptedException {
        try (MyResource res = new MyResource()) {
            // 강제로 예외 발생시키기
            throw new RuntimeException("강제 예외 발생");
        } catch (Exception e) {
	    e.printStackTrace();
        }
    }
}

class MyResource implements AutoCloseable {
    public void doSomething() {
    	System.out.println("리소스 사용 중...");
    }

    @Override
    public void close() {
    	System.out.println("리소스 정리 중(close 호출됨)");
    }
}

실행 결과 - close 호출되어 리소스 닫음

 

이와 같은 이유로 사용 후 꼭 회수(close)해야하는 자원을 다룰때는 try-with-resources를 사용하는것이 좋습니다.

반응형