1. 외부 자원

Java 프로그램이 실행될 때 때때로 Java 시스템 외부의 엔터티와 상호 작용합니다. 예를 들어, 디스크의 파일이 있습니다. 이러한 엔터티를 일반적으로 외부 리소스라고 합니다. 내부 리소스는 Java 시스템 내부에서 생성된 개체입니다.

일반적으로 상호 작용은 다음 체계를 따릅니다.

Try-with-resources 문

리소스 추적

운영 체제는 사용 가능한 리소스를 엄격하게 추적하고 다른 프로그램에서 리소스에 대한 공유 액세스를 제어합니다. 예를 들어, 한 프로그램이 파일을 변경하면 다른 프로그램은 해당 파일을 변경(또는 삭제)할 수 없습니다. 이 원칙은 파일에 국한되지 않지만 가장 쉽게 이해할 수 있는 예를 제공합니다.

운영 체제에는 프로그램이 리소스를 획득 및/또는 해제할 수 있도록 하는 기능(API)이 있습니다. 리소스가 사용 중인 경우 이를 획득한 프로그램만 작업할 수 있습니다. 리소스가 무료이면 모든 프로그램에서 리소스를 얻을 수 있습니다.

사무실에서 커피 머그를 공유한다고 상상해 보십시오. 누군가 머그잔을 가져가면 다른 사람들은 더 이상 머그잔을 가져갈 수 없습니다. 그러나 일단 머그를 사용하고 씻어서 제자리에 넣으면 누구나 다시 가져갈 수 있습니다. 버스나 지하철의 좌석 상황도 마찬가지다. 자리가 비어 있으면 누구나 탈 수 있습니다. 좌석이 점유된 경우 해당 좌석을 차지한 사람이 관리합니다.

외부 리소스 확보 .

Java 프로그램이 디스크의 파일 작업을 시작할 때마다 Java 시스템은 운영 체제에 독점 액세스를 요청합니다. 리소스가 비어 있으면 Java 시스템이 리소스를 획득합니다.

그러나 파일 작업을 마친 후에는 이 리소스(파일)를 해제해야 합니다. 즉, 더 이상 필요하지 않음을 운영 체제에 알려야 합니다. 이렇게 하지 않으면 프로그램에서 리소스를 계속 보유하게 됩니다.

운영 체제는 실행 중인 각 프로그램이 차지하는 리소스 목록을 유지 관리합니다. 프로그램이 할당된 리소스 제한을 초과하면 운영 체제는 더 이상 새 리소스를 제공하지 않습니다.

좋은 소식은 프로그램이 종료되면 모든 리소스가 자동으로 해제된다는 것입니다(운영 체제 자체에서 이 작업을 수행함).

나쁜 소식은 서버 애플리케이션을 작성하는 경우(그리고 많은 서버 애플리케이션이 Java로 작성되는 경우) 서버가 중단 없이 며칠, 몇 주, 몇 달 동안 실행될 수 있어야 한다는 것입니다. 그리고 하루에 100개의 파일을 열고 닫지 않으면 몇 주 후에 응용 프로그램이 리소스 제한에 도달하고 충돌합니다. 그것은 안정적인 작업의 몇 달에 훨씬 못 미치는 것입니다.


2. close()방법

외부 리소스를 사용하는 클래스에는 리소스를 해제하는 특별한 방법이 있습니다 close().

아래에서 우리는 파일에 무언가를 쓴 다음 완료되면 파일을 닫는 프로그램의 예를 제공합니다. 즉, 운영 체제의 리소스를 해제합니다. 다음과 같이 보입니다.

암호 메모
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
파일 경로입니다.
파일 개체 가져오기: 리소스를 가져옵니다.
파일에 쓰기
파일 닫기 - 리소스 해제

파일(또는 다른 외부 리소스)로 작업한 후에는 close()외부 리소스에 연결된 개체에서 메서드를 호출해야 합니다.

예외

모든 것이 간단해 보입니다. 그러나 프로그램이 실행될 때 예외가 발생할 수 있으며 외부 리소스가 해제되지 않습니다. 그리고 그것은 매우 나쁩니다.

메서드가 항상 호출 되도록 하려면 코드를 - - 블록으로 래핑하고 블록 에 메서드를 추가 close()해야 합니다 . 다음과 같이 표시됩니다.trycatchfinallyclose()finally

try
{
   FileOutputStream output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

output이 코드는 변수가 블록 내부에서 선언되어 블록 try {}에서 볼 수 없기 때문에 컴파일되지 않습니다 finally.

수정하자:

FileOutputStream output = new FileOutputStream(path);

try
{
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

괜찮지만 FileOutputStream개체를 ​​생성할 때 오류가 발생하면 작동하지 않으며 이는 매우 쉽게 발생할 수 있습니다.

수정하자:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

여전히 몇 가지 비판이 있습니다. FileOutputStream첫째, 객체를 생성할 때 오류가 발생하면 output변수는 null이 됩니다. 이 가능성은 블록에서 설명되어야 합니다 finally.

둘째, close()메소드는 항상 블록에서 호출되며 finally이는 try블록에서 필요하지 않음을 의미합니다. 최종 코드는 다음과 같습니다.

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   if (output != null)
      output.close();
}

생략할 수 있는 블록을 고려하지 않아도 catch3줄의 코드가 10이 됩니다. 하지만 기본적으로 파일을 열고 1을 작성했습니다. 조금 번거롭지 않나요?


3. try리소스와 함께

그리고 여기에서 Java의 제작자는 우리에게 약간의 구문 설탕을 뿌리기로 결정했습니다. 7번째 버전부터 Java에는 새로운 try-with-resources 문이 있습니다.

메서드에 대한 필수 호출 문제를 정확하게 해결하기 위해 만들어졌습니다 close(). 일반적인 경우는 매우 간단해 보입니다.

try (ClassName name = new ClassName())
{
     Code that works with the name variable
}

이것은 try 진술 의 또 다른 변형입니다 . 키워드 뒤에 괄호를 추가한 try다음 괄호 안에 외부 리소스가 있는 개체를 만들어야 합니다. 괄호 안의 각 개체에 대해 컴파일러는 finally섹션과 메서드 호출을 추가합니다 close().

다음은 두 가지 동등한 예입니다.

긴 코드 try-with-resources가 포함된 코드
FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
finally
{
   if (output != null)
   output.close();
}
try(FileOutputStream output = new FileOutputStream(path))
{
   output.write(1);
}

-with-resources를 사용하는 코드는 try훨씬 짧고 읽기 쉽습니다. 코드가 적을수록 오타나 기타 오류가 발생할 가능성이 줄어듭니다.

그런데 -with-resources 문 에 블록을 catch추가 할 수 있습니다 . 또는 필요하지 않은 경우 추가할 수 없습니다.finallytry



4. 동시에 여러 변수

그런데 동시에 여러 파일을 열어야 하는 상황이 자주 발생할 수 있습니다. 파일을 복사한다고 가정해 보겠습니다. 따라서 데이터를 복사하는 원본 파일과 데이터를 복사하는 파일의 두 개체가 필요합니다.

이 경우 try-with-resources 문을 사용하면 그 안에 하나가 아닌 여러 개체를 만들 수 있습니다. 개체를 만드는 코드는 세미콜론으로 구분해야 합니다. 이 진술의 일반적인 모습은 다음과 같습니다.

try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
   Code that works with the name and name2 variables
}

파일 복사의 예:

긴 코드 짧은 코드
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

FileInputStream input = null;
FileOutputStream output = null;

try
{
   input = new FileInputStream(src);
   output = new FileOutputStream(dest);

   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
finally
{
   if (input != null)
      input.close();
   if (output != null)
      output.close();
}
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);

FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}

글쎄, 우리는 여기서 무엇을 말할 수 있습니까? try-with-resources는 멋진 것입니다!


5. AutoCloseable인터페이스

하지만 그게 다가 아닙니다. 주의 깊은 독자는 즉시 이 진술이 적용될 수 있는 방법을 제한하는 함정을 찾기 시작할 것입니다.

그러나 try클래스에 메서드가 없는 경우 -with-resources 문은 어떻게 작동합니까 close()? 글쎄, 아무 것도 호출되지 않는다고 가정하십시오. 방법도, 문제도 없습니다.

그러나 try클래스에 여러 close()메서드가 있는 경우 -with-resources 문은 어떻게 작동합니까? 그리고 그들에게 전달할 인수가 필요합니까? 클래스에 close()매개변수가 없는 메서드가 없나요?

나는 당신이 이러한 질문들과 아마도 여전히 다른 질문들을 정말로 스스로에게 물어봤기를 바랍니다.

이러한 문제를 피하기 위해 Java 작성자는 매개 변수가 없는 AutoCloseable단 하나의 메서드만 있는 이라는 특수 인터페이스를 고안했습니다 .close()

또한 -with-resources 문 에서 구현하는 클래스의 개체만AutoCloseable 리소스로 선언할 수 있다는 제한을 추가했습니다 . try결과적으로 이러한 개체에는 항상 매개 close()변수가 없는 메서드가 있습니다.

그런데 -with-resources 문이 클래스에 매개 변수가 없는 자체 메서드가 있지만 구현하지 않는 try개체를 리소스로 선언하는 것이 가능하다고 생각하십니까 ?close()AutoCloseable

나쁜 소식: 정답은 아니오입니다. 클래스는 AutoCloseable인터페이스를 구현해야 합니다.

희소식: Java에는 이 인터페이스를 구현하는 많은 클래스가 있으므로 모든 것이 제대로 작동할 가능성이 높습니다.