Java Category/Java

[JAVA] 리소스(resource) 자동 닫기

ReBugs 2023. 7. 25.

이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다. 


  • 리소스란 데이터를 제공하는 객체(DB 등)를 뜻한다.
  • 리소스를 사용하기 위해선 열어야 하고, 사용이 끝나면 반드시 닫아야 한다.
  • 리소스를 사용하고 닫지 않으면 불안정한 상태로 남게 된다.

 

try-catch-finally문

리소스를 자동으로 닫는 기본적인 방법은 try-catch-finally문에서 finally 부분에 리소스를 닫는 코드를 넣어주면 된다.

finally는 예외가 발생하든 안 하든, 메소드가 return문을 만나도 무조건 실행되기 때문이다.

FileInputStream fis = null;
    try {
        fis = new FileInputStream("file.tsxt");
    } catch(IOException e) {
        //예외 처리 코드 작성
    }finally {
        fis.close(); //리소스 닫기
    }

 


 

try-with-resources문

try-catch-finally문과 다르게 finally문을 작성하지 않아도 된다.

이 또한 예외 발생 여부와 상관없이 리소스를 자동으로 닫아준다.

try괄호에 리소스를 여는 코드를 작성하면 try 블록이 정상적으로 실행을 완료했거나 도중에 예외가 발생하면 자동으로 리소스의 close() 메소드가 호출된다.

 

하지만 try-with-resources문을 사용하려면 리소스는 java.lang.AutoCloseable 인터페이스를 구현하고 해당 인터페이스의 close() 메소드를 오버라이딩(재정의) 해야 한다.

java.lang.AutoCloseable 인터페이스는 자바가 기본적으로 제공한다.
따라서 우리는 구현 객체만 만들면 된다.

MyResource.java

public class MyResource implements AutoCloseable{
	//...
	@Override
	public void close() throws Exception {
		//리소스를 닫는 코드 작성
	}

}

 

Main.java

public class Main {
	public static void main(String[] args) {
		try {
			MyResource fis1 = new MyResource("file1.txt");
			MyResource fis2 = new MyResource("file2.txt");
		}catch(IOException e) {
			//예외 처리 코드 작성
		}
        //finally문을 작성하지 않아도 자동으로 리소스가 닫힌다.
	}
}
Java 9 버전 이후부터는  외부 리소스 변수를 사용할 수 있다.
public class Main {
	public static void main(String[] args) {
		MyResource fis1 = new MyResource("file1.txt");
		MyResource fis2 = new MyResource("file2.txt");
		try (fis1; fis2){
			
		}catch(IOException e) {
			//예외 처리 코드 작성
		}
	}
}​

 

아래의 예제는 try-with-resources문을 이용하여 제대로 리소스를 닫을 수 있는지를 확인하는 예제이다.

결과를 확인하면 제대로 예외가 발생해도 close() 메소드가 자동으로 호출되어 리소스를 자동으로 닫을 수 있다는 것을 확인할 수 있다.

 

MyResource.java

public class MyResource implements AutoCloseable {
	private String name;
	
	public MyResource(String name) {
		this.name = name;
		System.out.println("[MyResource(" + name + ") 열기]");
	}
	
	public String read1() {
		System.out.println("[MyResource(" + name + ") 읽기]");
		return "100";
	}
	
	public String read2() {
		System.out.println("[MyResource(" + name + ") 읽기]");
		return "abc";
	}
	
	@Override
	public void close() throws Exception {
		System.out.println("[MyResource(" + name + ") 닫기]");
	}
}

 

Main.java

public class Main {
	public static void main(String[] args) {
		try (MyResource res = new MyResource("A")) {
			String data = res.read1();
			int value = Integer.parseInt(data);
		} catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
		System.out.println();
        
		///////////////////////////////////////////////////
        
		try (MyResource res = new MyResource("A")) {
			String data = res.read2();
			//NumberFormatException 발생
			int value = Integer.parseInt(data);
		} catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
		System.out.println();
        
		///////////////////////////////////////////////////
        
		try (
			MyResource res1 = new MyResource("A"); 
			MyResource res2 = new MyResource("B")
		) {
			String data1 = res1.read1();
			String data2 = res2.read1();
		} catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
		System.out.println();
        
		///////////////////////////////////////////////////
        
		MyResource res1 = new MyResource("A"); 
		MyResource res2 = new MyResource("B");
		try (res1; res2) {
			String data1 = res1.read1();
			String data2 = res2.read1();
		} catch(Exception e) {
			System.out.println("예외 처리: " + e.getMessage());
		}
	}
}
/*
[MyResource(A) 열기]
[MyResource(A) 읽기]
[MyResource(A) 닫기]

[MyResource(A) 열기]
[MyResource(A) 읽기]
[MyResource(A) 닫기]
예외 처리: For input string: "abc"

[MyResource(A) 열기]
[MyResource(B) 열기]
[MyResource(A) 읽기]
[MyResource(B) 읽기]
[MyResource(B) 닫기]
[MyResource(A) 닫기]

[MyResource(A) 열기]
[MyResource(B) 열기]
[MyResource(A) 읽기]
[MyResource(B) 읽기]
[MyResource(B) 닫기]
[MyResource(A) 닫기]
*/

댓글