리소스 사용 블록에서 여러 개의 연결된 리소스를 관리하기 위한 올바른 관용구입니까?
Java 7 리소스 사용 구문(ARM 블록(Automatic Resource Management)이라고도 함)은 1개만 사용할 경우 적절하고 짧고 간단합니다.AutoCloseable
그런데된 여러 해야 할 때, 예를 서로종속된 여러 을 선언해야 할 때 잘 .FileWriter
a. a. a.BufferedWriter
은 물론 어떤 경우에나 해당되는 입니다.AutoCloseable
리소스는 이 두 개의 특정 클래스뿐만 아니라 래핑됩니다.
다음 세 가지 대안을 생각해 냈습니다.
1)
지금까지 본 순진한 관용구는 ARM 관리 변수의 최상위 래퍼만 선언하는 것입니다.
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
이것은 좋고 짧지만, 고장났습니다.가 되는 「」는,FileWriter
된 변수에서 . 이치노finally
차단합니다. 이 차단은 다음 경로를 통해서만 닫힙니다.close
BufferedWriter
「」, 「」로부터했을 경우입니다.bw
의 컨스트럭터,그 컨스트럭터close
되지 않기 에, 이 되는 「」, 「」, 「」가 호출되지 않습니다.FileWriter
닫히지 않습니다.
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
경우 ARM 관리변수로둘다ARM은 .fw.close()
두 번 호출됩니다.직접 호출뿐만 아니라 포장을 통해서도bw.close()
.
은, 모두 「」, 「」의 「2」를 실장하고 , 에서는 문제가 않습니다.Closeable
입니다.AutoCloseable
에는, 복수의 이 「」, 「」, 「」에의 이 기재되어 있습니다.close
용됩니니다다
이 스트림을 닫고 관련된 시스템리소스를 해방합니다.스트림이 이미 닫혀 있는 경우 이 메서드를 호출해도 효과가 없습니다.
저는 '', '어느 정도', '어느 정도', '어느 정도'만 구현하는 자원을 수 .AutoCloseable
아니다)Closeable
>, >, >, >, >, >, >를 보증하지 않습니다close
번 수 .
java.io 의 클로즈 방식과는 다릅니다.닫기 가능, 이 닫기 메서드는 유휴할 필요가 없습니다.즉, 이 닫기 메서드를 여러 번 호출하면 여러 번 호출해도 효과가 없는 Closeable.close와는 달리 눈에 보이는 부작용이 발생할 수 있습니다.다만, 이 인터페이스의 실장자는, 가까운 메서드를 유효하게 하는 것을 강하게 추천합니다.
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
정확해야 . 이 버전은 '만'이기 입니다.fw
는 청소할 필요가 있는 실제 리소스를 나타냅니다.bw
이 없고, ''을 할 뿐이다.★★★★★★★★★★★★★★★★★★,fw
에 있는 것을 fw
.
한편, 구문이 조금 불규칙하고, 또한 Eclipse가 경고를 보냅니다.이 경고는 잘못된 경보라고 생각합니다만, 아직 대처하지 않으면 안 되는 경고입니다.
리소스 누출: 'bw'는 닫히지 않습니다.
그럼 어떤 접근방식을 택해야 할까요?아니면 제가 다른 사자성어를 놓쳤나요?
대안에 대한 저의 견해는 다음과 같습니다.
1)
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
}
15년 전 기존 C++에서 Java로 온 가장 좋은 점은 프로그램을 신뢰할 수 있다는 것입니다.비록 그들이 종종 하는 것처럼 일이 더러워서 잘못되더라도, 저는 나머지 코드가 최상의 행동과 장미 향기를 띠길 바랍니다.말말,, indeed indeed indeed는,BufferedWriter
을 사용법예를 들어 메모리가 부족해지는 것은 드문 일이 아닙니다.데코레이터분들은 데코레이션인지 ?java.io
래퍼 클래스는 컨스트럭터에서 체크된 예외를 발생시킵니다. 애매한지식에 이 되지 그런 모호한 지식에 의존한다면 코드를 이해하는데 별로 도움이 되지 않습니다.
또한 "파괴"도 있다.에러 상태가 있는 경우는, 삭제가 필요한 파일(표시되어 있지 않은 파일의 코드)에 폐기물을 플러시 하고 싶지 않을 가능성이 있습니다.물론 파일을 삭제하는 것도 에러 처리로서 할 수 있는 또 하나의 흥미로운 조작입니다.
일반적으로 당신이 원하는 것은finally
블록은 가능한 한 짧고 신뢰할 수 있어야 합니다.플러시를 추가하는 것은 이 목표에 도움이 되지 않습니다.많은 릴리스에서 JDK의 버퍼링 클래스 중 일부는 예외로 인해flush
이내에close
야기했다close
이름을 붙일 수 없는 장식물.이 문제는 잠시 수정되었지만 다른 구현에서 이를 기대할 수 있습니다.
2)
try (
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)
) {
bw.write(text);
}
우리는 여전히 암묵적인 최종 블록에서 플러싱하고 있다(지금 반복됨)close
- 데코레이터를 추가할수록 더 악화됩니다.) 하지만 시공은 안전하고 최종적으로 암묵적으로 차단해야 하기 때문에 실패하더라도flush
는 리소스 릴리스를 방해하지 않습니다.
3)
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
}
여기 벌레가 있어요.다음 항목이어야 합니다.
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
bw.flush();
}
제대로 구현되지 않은 일부 장식품들은 사실 자원이기 때문에 안정적으로 닫아야 합니다.또한 일부 스트림은 특정 방식으로 닫아야 할 수 있습니다(압축 작업을 하고 있고 종료하기 위해 비트를 써야 하며 모든 것을 플러시할 수는 없습니다).
평결
3이 기술적으로 뛰어난 솔루션이지만 소프트웨어 개발상의 이유로 2가 더 나은 선택입니다.단, 리소스로의 시행은 여전히 불충분한 수정이므로 Java SE 8에서는 닫힘이 있는 구문이 보다 명확해야 하는 Execute Around 숙어를 사용해야 합니다.
첫 번째 스타일은 Oracle에서 제안하는 스타일입니다. BufferedWriter
는 체크된 예외를 발생시키지 않기 때문에 예외가 발생해도 프로그램은 복구되지 않을 것으로 예상되므로 리소스 복구가 대부분 중단됩니다.
대부분 스레드 내에서 발생할 수 있고 스레드는 소멸되지만 프로그램은 계속 진행됩니다.예를 들어, 프로그램의 나머지 부분에 심각한 손상을 줄 정도로 짧은 일시적인 메모리 정지가 있었습니다.다만, 이것은 꽤 궁지에 몰린 경우이며, 자원 누수가 문제가 될 정도로 자주 발생하는 경우, 자원 활용이 가장 작은 문제입니다.
옵션 4
가능한 경우 리소스를 자동 닫기가 아닌 닫기로 변경하십시오.컨스트럭터를 체인으로 연결할 수 있다는 것은 리소스를 두 번 닫는다는 것을 의미합니다.(이는 ARM 이전에도 마찬가지였습니다.)자세한 것은, 이하를 참조해 주세요.
옵션 5
close()가 두 번 호출되지 않도록 ARM과 코드를 주의 깊게 사용하지 마십시오.
옵션 6
ARM을 사용하지 말고 최종적으로 close() 콜을 직접 시도/캐치합니다.
이 문제가 ARM만의 문제가 아니라고 생각하는 이유
이러한 예에서 finally close() 콜은 모두 캐치블록 안에 있어야 합니다.읽기 쉽도록 생략.
fw가 두 번 닫힐 수 있기 때문에 좋지 않습니다.(FileWriter에는 문제가 없지만 가상 예에서는 문제가 없습니다).
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if ( fw != null ) fw.close();
if ( bw != null ) bw.close();
}
BufferedWriter를 구성할 때 예외가 발생하면 fw가 닫히지 않으므로 사용할 수 없습니다.(다시 말씀드리지만, 일어날 수 없습니다만, 가정적인 예에서는):
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if ( bw != null ) bw.close();
}
이전 코멘트와 동의하는 방법: 가장 간단한 방법은 (2)입니다.Closeable
resources 및 try-with-time 절에서 순서대로 선언합니다.만약 당신이AutoCloseable
를 체크하기만 하면, 그것들을 다른 (표준) 클래스로 랩 할 수 있습니다.close
1회(Facade Pattern)만 호출됩니다(예:private bool isClosed;
실제로 Oracle도 (1) 컨스트럭터를 체인으로 하고 체인을 통해 예외를 올바르게 처리하지 않습니다.
또는 스태틱팩토리 방식을 사용하여 체인된 리소스를 수동으로 작성할 수 있습니다.이 방법에서는 체인이 캡슐화되어 중간에서 장애가 발생했을 경우의 정리가 처리됩니다.
static BufferedWriter createBufferedWriterFromFile(File file)
throws IOException {
// If constructor throws an exception, no resource acquired, so no release required.
FileWriter fileWriter = new FileWriter(file);
try {
return new BufferedWriter(fileWriter);
} catch (IOException newBufferedWriterException) {
try {
fileWriter.close();
} catch (IOException closeException) {
// Exceptions in cleanup code are secondary to exceptions in primary code (body of try),
// as in try-with-resources.
newBufferedWriterException.addSuppressed(closeException);
}
throw newBufferedWriterException;
}
}
그런 다음 리소스 시도 절에서 단일 리소스로 사용할 수 있습니다.
try (BufferedWriter writer = createBufferedWriterFromFile(file)) {
// Work with writer.
}
복잡성은 여러 예외를 처리할 때 발생합니다.그렇지 않으면 "지금까지 취득한 밀접한 자원"에 불과합니다.일반적인 방법은 리소스를 보유하는 개체를 보유하는 변수를 먼저 초기화하는 것입니다.null
(여기서)fileWriter
그런 다음 null 체크를 청소에 포함시키지만, 그럴 필요는 없을 것 같습니다.컨스트럭터에 장애가 발생해도 청소할 것이 없기 때문에 예외를 전파할 수 있기 때문에 코드가 약간 간소화됩니다.
일반적으로 다음과 같이 할 수 있습니다.
static <T extends AutoCloseable, U extends AutoCloseable, V>
T createChainedResource(V v) throws Exception {
// If constructor throws an exception, no resource acquired, so no release required.
U u = new U(v);
try {
return new T(u);
} catch (Exception newTException) {
try {
u.close();
} catch (Exception closeException) {
// Exceptions in cleanup code are secondary to exceptions in primary code (body of try),
// as in try-with-resources.
newTException.addSuppressed(closeException);
}
throw newTException;
}
}
마찬가지로 3개의 자원 등을 체인으로 할 수 있습니다.
수학적인 측면에서는 한 번에 두 개의 리소스를 연결함으로써 세 번 체인할 수도 있습니다. 즉, 컨스트럭터가 연관성이 있기 때문에 성공했을 때 동일한 객체를 얻을 수 있습니다.또한 컨스트럭터에 오류가 발생한 경우에도 예외는 동일합니다.위의 체인에 S를 추가했다고 가정하면(따라서 U, T, S를 차례로 적용하여 S로 끝납니다), 첫 번째 체인 S와 T, 그리고 (ST)U에 대응하는 U, 또는 첫 번째 체인 T와 U, 그 후 S(STU)에 대응하는 S 중 하나가 됩니다.그러나 단일 공장 기능에 명시적인 3중 체인을 작성하는 것이 더 명확합니다.
리소스가 중첩되므로 try-with 구도 다음과 같아야 합니다.
try (FileWriter fw=new FileWriter(file)) {
try (BufferedWriter bw=new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
} catch (IOException ex) {
// handle ex
}
저는 단지 ARM을 사용하지 않고 FileWriter를 정확히 한 번 닫아야 한다는 Jeanne Boyarsky의 제안을 바탕으로 하고 싶었습니다.여기에 문제가 없다고 생각하지 마세요.
FileWriter fw = null;
BufferedWriter bw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
bw.write(text);
} finally {
if (bw != null) bw.close();
else if (fw != null) fw.close();
}
ARM은 통사적인 설탕이기 때문에 최종 블록을 대체하기 위해 항상 사용할 수는 없습니다.반복기로 가능한 일을 하기 위해 각 루프를 항상 사용할 수 있는 것은 아닙니다.
ARM을 사용하지 말고 Closeable로 진행하도록 하겠습니다.다음과 같은 방법을 사용합니다.
public void close(Closeable... closeables) {
for (Closeable closeable: closeables) {
try {
closeable.close();
} catch (IOException e) {
// you can't much for this
}
}
}
또, 다음의 장소에 전화하는 것도 고려해 주세요.BufferedWriter
그것은 단지 가까운 사람들에게만 위임하는 것이 아니기 때문에FileWriter
, 그러나 그것은 다음과 같은 몇 가지 청소를 합니다.flushBuffer
.
이 솔루션은 다음과 같이 "추출 방법" 리팩터링을 수행하는 것입니다.
static AutoCloseable writeFileWriter(FileWriter fw, String txt) throws IOException{
final BufferedWriter bw = new BufferedWriter(fw);
bw.write(txt);
return new AutoCloseable(){
@Override
public void close() throws IOException {
bw.flush();
}
};
}
printToFile
어느 쪽이든 쓸 수도 있다
static void printToFile(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
AutoCloseable w = writeFileWriter(fw, text);
w.close();
} catch (Exception ex) {
// handle ex
}
}
또는
static void printToFile(String text, File file) {
try (FileWriter fw = new FileWriter(file);
AutoCloseable w = writeFileWriter(fw, text)){
} catch (Exception ex) {
// handle ex
}
}
class lib designers를 위해, 나는 그들에게 확장하라고 제안할 것이다.AutoClosable
추가 메서드와 인터페이스하여 닫힘을 억제합니다.이 경우 닫힘 동작을 수동으로 제어할 수 있습니다.
언어 디자이너에게 교훈은 새로운 기능을 추가하는 것은 다른 기능을 많이 추가하는 것을 의미할 수 있다는 것입니다.이 Java의 경우 자원 소유권 전송 메커니즘에서 ARM 기능이 더 잘 작동합니다.
갱신하다
원래 위의 코드는@SuppressWarning
그 이후BufferedWriter
필요한 기능 내close()
.
코멘트에서 제시된 바와 같이flush()
작가를 닫기 전에 부름을 받으려면 먼저 부름을 받아야 합니다.return
(명시적 또는 명시적) 스테이트먼트.발신자가 이 작업을 확실히 할 수 있는 방법은 현재 없기 때문에, 이것은 다음에 대해 문서화되어 있을 필요가 있습니다.writeFileWriter
.
다시 업데이트
위의 업데이트는 다음과 같습니다.@SuppressWarning
발신자에게 자원을 반환하는 기능이 필요하기 때문에, 그 자체를 닫을 필요는 없습니다.유감스럽게도, 이것으로 상황의 선두로 돌아옵니다.즉, 경고가 발신측으로 돌아옵니다.
따라서 이 문제를 적절하게 해결하려면 맞춤형으로 만들어야 합니다.AutoClosable
닫힐 때마다 밑줄은BufferedWriter
이 되어야 한다flush()
ed. 사실, 이것은 경고를 우회하는 또 다른 방법을 보여줍니다.BufferWriter
어떤 식으로든 닫히지 않습니다.
언급URL : https://stackoverflow.com/questions/12552863/correct-idiom-for-managing-multiple-chained-resources-in-try-with-resources-bloc
'programing' 카테고리의 다른 글
Null 포인터에 주소 0이 사용되는 이유는 무엇입니까? (0) | 2022.05.30 |
---|---|
Vuex 돌연변이 및 작업이 작동하지 않음 (0) | 2022.05.30 |
구성 요소에서 vuex에 API 데이터 커밋 (0) | 2022.05.30 |
Vue 2 AOT 사전 컴파일 (0) | 2022.05.30 |
vuex getter의 Nuxt 플러그인 기능에 액세스하는 중 (0) | 2022.05.30 |