finally 블록에서 예외 발생


100

finally블록 에서 발생하는 예외를 처리하는 우아한 방법이 있습니까?

예를 들면 :

try {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
finally {
   try{
     resource.close();
   }
   catch( Exception ex ) {
     // Could not close the resource?
   }
}

블록 에서 try/ catch를 어떻게 피 finally합니까?

답변:


72

나는 보통 다음과 같이한다.

try {
  // Use the resource.
} catch( Exception ex ) {
  // Problem with the resource.
} finally {
  // Put away the resource.
  closeQuietly( resource );
}

기타 :

protected void closeQuietly( Resource resource ) {
  try {
    if (resource != null) {
      resource.close();
    }
  } catch( Exception ex ) {
    log( "Exception during Resource.close()", ex );
  }
}

4
네, 저는 매우 유사한 관용구를 사용합니다. 그러나 나는 그것을위한 함수를 만들지 않는다.
OscarRyz

9
같은 클래스의 몇 군데에서 관용구를 사용해야하는 경우 함수가 편리합니다.
Darron

null 검사는 중복됩니다. 리소스가 null 인 경우 호출 메서드가 중단 된 경우 수정해야합니다. 또한 리소스가 null이면 기록되어야합니다. 그렇지 않으면 잠재적 인 예외가 자동으로 무시됩니다.
Dave Jarvis

14
null 검사가 항상 중복되는 것은 아닙니다. "resource = new FileInputStream ("file.txt ")"를 시도의 첫 번째 줄로 생각하십시오. 또한이 질문은 많은 사람들이 사용하지 않는 측면 지향 프로그래밍에 관한 것이 아닙니다. 그러나 Exception을 무시해서는 안된다는 개념은 로그 문을 보여줌으로써 가장 간결하게 처리되었습니다.
Darron

1
Resource=> Closeable?
Dmitry Ginzburg

25

나는 일반적으로 다음 closeQuietly방법 중 하나를 사용합니다 org.apache.commons.io.IOUtils.

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}

3
Closeable public static void closeQuietly (Closeable closeable) {
Peter Lawrey

6
예, Closeable이 좋습니다. JDBC 자원과 같은 많은 것들이 그것을 구현하지 않는 것은 부끄러운 일입니다.
Darron

22

Java 7을 사용하고를 resource구현하는 AutoClosable경우 다음을 수행 할 수 있습니다 (예 : InputStream 사용).

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}

8

의심의 여지가 있지만 예외가 발생하도록하고 메서드 내에서 아무것도 기록 할 수없는 경우 유용 할 수 있습니다 (예 : 라이브러리이므로 호출 코드가 예외 및 로깅을 처리하도록하려는 경우).

Resource resource = null;
boolean isSuccess = false;
try {
    resource = Resource.create();
    resource.use();
    // Following line will only run if nothing above threw an exception.
    isSuccess = true;
} finally {
    if (resource != null) {
        if (isSuccess) {
            // let close throw the exception so it isn't swallowed.
            resource.close();
        } else {
            try {
                resource.close();
            } catch (ResourceException ignore) {
                // Just swallow this one because you don't want it 
                // to replace the one that came first (thrown above).
            }
        }
    }
}

업데이트 : 나는 이것에 대해 조금 더 조사했고 이것에 대해 나보다 더 명확하게 생각한 누군가로부터 훌륭한 블로그 게시물을 발견했습니다 : http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make -mess-of-stream.html 그는 한 단계 더 나아가 두 예외를 하나로 결합하여 어떤 경우에 유용하다는 것을 알 수 있습니다.


1
블로그 링크에 대한 +1. 또한 최소한 ignore예외를 기록합니다
Denis Kniazhev 2011 년

6

Java 7에서는 더 이상 finally 블록 에서 리소스를 명시 적으로 닫을 필요가 없습니다. 대신 try -with-resources 구문을 사용할 수 있습니다 . try-with-resources 문은 하나 이상의 리소스를 선언하는 try 문입니다. 리소스는 프로그램이 완료된 후 닫아야하는 개체입니다. try-with-resources 문은 문 끝에서 각 리소스가 닫히도록합니다. java.io.Closeable을 구현하는 모든 객체를 포함하는 java.lang.AutoCloseable을 구현하는 모든 객체를 리소스로 사용할 수 있습니다.

다음 코드를 가정하십시오.

try( Connection con = null;
     Statement stmt = con.createStatement();
     Result rs= stmt.executeQuery(QUERY);)
{  
     count = rs.getInt(1);
}

예외가 발생하면 이 세 가지 리소스 각각에 대해 생성 된 순서와 반대로 close 메서드가 호출됩니다. 이는 close 메소드가 ResultSetm에 대해 먼저 호출 된 다음 Statement에 대해 그리고 마지막에 Connection 객체에 대해 호출됨을 의미합니다.

close 메서드가 자동으로 호출 될 때 발생하는 모든 예외가 억제된다는 것을 아는 것도 중요합니다. 이러한 억제 된 예외는 Throwable 클래스에 정의 된 getsuppressed () 메서드 로 검색 할 수 있습니다 .

출처 : https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html


이 답변 이이 접근 방식과 OP의 게시 된 예제 코드가 작동하는 방식 간의 동작 차이를 언급하지 않는 것은 불완전한 것 같습니다.
Nathan Hughes

2
try-with-resources를 사용하면 try 블록의 부분이 정상적으로 완료되지만 OP 코드가 수행하는 것과 달리 close 메서드가 그렇지 않은 경우 닫기시 예외가 발생합니다. 행동의 변화를 인정하지 않고 그것을 대체물로 추천하는 것은 잠재적으로 오해의 소지가있는 것처럼 보입니다.
Nathan Hughes

예외가 발생하지 않으며 close 메서드가 자동으로 호출되지 않습니다.
Soroosh 2015

2
내가 설명한 사례를 시도하십시오. try 블록은 정상적으로 완료되고 close는 무언가를 던집니다. 링크를 게시 한 페이지를 다시 읽으면 try 블록이 무언가를 던질 때만 억제가 적용됩니다.
Nathan Hughes

3

'최종'블록에서 발생하는 예외를 무시하는 것은 일반적으로 예외가 무엇이고 어떤 조건을 나타낼 지 알지 못하는 한 일반적으로 나쁜 생각 입니다. 정상적인 try/finally사용 패턴에서 try블록은 외부 코드가 기대하지 않는 상태로 사물을 배치하고 finally블록은 이러한 사물의 상태를 외부 코드가 예상하는 상태로 복원합니다. 예외를 포착하는 외부 코드는 일반적으로 예외에도 불구하고 모든 것이normal상태. 예를 들어, 일부 코드가 트랜잭션을 시작한 다음 두 개의 레코드를 추가하려고한다고 가정합니다. "finally"블록은 "커밋되지 않은 경우 롤백"작업을 수행합니다. 호출자는 두 번째 "추가"작업을 실행하는 동안 예외가 발생하도록 준비 할 수 있으며 이러한 예외를 포착하면 데이터베이스가 두 작업을 시도하기 전의 상태가 될 것으로 예상 할 수 있습니다. 그러나 롤백 중에 두 번째 예외가 발생하면 호출자가 데이터베이스 상태에 대해 가정하면 나쁜 일이 발생할 수 있습니다. 롤백 실패는 단순한 "레코드 추가 실패"예외를 예상하는 코드로 포착해서는 안되는 중대한 위기를 나타냅니다 .

내 개인적인 성향은 finally 메서드가 발생하는 예외를 포착하고 "CleanupFailedException"으로 래핑하여 이러한 실패가 주요 문제를 나타내며 그러한 예외를 가볍게 포착해서는 안된다는 것을 인식하는 것입니다.


2

두 개의 예외가 두 개의 다른 클래스 인 경우 하나의 솔루션

try {
    ...
    }
catch(package1.Exception err)
   {
    ...
   }
catch(package2.Exception err)
   {
   ...
   }
finally
  {
  }

그러나 때로는이 두 번째 시도를 피할 수 없습니다. 예 : 스트림 닫기

InputStream in=null;
try
 {
 in= new FileInputStream("File.txt");
 (..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
 }
catch(SQLException err)
 {
 //handle exception
 }
finally
 {
 //at the end, we close the file
 if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
 }

귀하의 경우 "using"문을 사용했다면 리소스를 정리해야합니다.
Chuck Conway

내 잘못은 C #이라고 가정하고 있습니다.
Chuck Conway

1

추가 차단을 피하고 싶은 이유는 무엇입니까? finally 블록에는 예외를 발생시킬 수있는 "정상"작업이 포함되어 있고 finally 블록이 완전히 실행되기를 원하므로 예외를 포착해야합니다.

finally 블록이 예외를 던질 것으로 예상하지 않고 예외를 처리하는 방법을 모르는 경우 (스택 추적 만 덤프 할 것임) 예외가 호출 스택을 위로 올리도록합니다 (finally 블록에서 try-catch 제거 블록).

타이핑을 줄이고 싶다면 finally 블록에서 발생하는 모든 예외를 포착하는 "전역"외부 try-catch 블록을 구현할 수 있습니다.

try {
    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }
} catch (Exception ex) {
    ...
}

2
-1 이것도. 하나의 finally 블록에서 여러 리소스를 닫으려고하면 어떻게 되나요? 첫 번째 리소스 닫기가 실패하면 예외가 throw되면 다른 리소스는 열린 상태로 유지됩니다.
Outlaw Programmer

이것이 내가 Paul에게 finally 블록이 완료되도록하려면 예외를 잡아야한다고 말한 이유입니다. 전체 답변을 읽으십시오!
Eduard Wirch

1

많은 고려 끝에 다음 코드가 가장 좋습니다.

MyResource resource = null;
try {
    resource = new MyResource();
    resource.doSomethingFancy();
    resource.close(); 
    resource = null;  
} finally {
    closeQuietly(resource)
}

void closeQuietly(MyResource a) {
    if (a!=null)
        try {
             a.close();
        } catch (Exception e) {
             //ignore
        }
}

이 코드는 다음을 보장합니다.

  1. 코드가 완료되면 리소스가 해제됩니다.
  2. 리소스를 닫을 때 throw되는 예외는 처리하지 않고는 사용되지 않습니다.
  3. 코드는 리소스를 두 번 닫으려고하지 않으며 불필요한 예외가 생성되지 않습니다.

resource.close (); 호출을 피할 수도 있습니다. try 블록의 resource = null, 즉 finally 블록의 용도입니다. 또한 "멋진 작업을 수행"하는 동안 발생한 예외를 처리하지 않는다는 점에 유의하십시오. 실제로 스택 아래의 더 높은 응용 프로그램 수준에서 인프라 예외를 처리하는 것이 더 좋습니다.
Paul

resource.close ()도 throw 및 예외가 발생할 수 있습니다. 즉, 버퍼 플러시가 실패 할 때입니다. 이 예외는 사용해서는 안됩니다. 그러나 이전에 발생한 예외의 결과로 스트림을 닫는 경우 리소스는 예외를 무시하고 근본 원인을 유지하면서 조용히 닫혀 야합니다.
Grogi 2013-04-24

0

가능한 경우 오류 조건을 피하기 위해 테스트해야합니다.

try{...}
catch(NullArgumentException nae){...}
finally
{
  //or if resource had some useful function that tells you its open use that
  if (resource != null) 
  {
      resource.Close();
      resource = null;//just to be explicit about it was closed
  }
}

또한 복구 할 수있는 예외 만 포착해야합니다. 복구 할 수없는 경우 프로그램의 최상위 수준으로 전파됩니다. 오류 조건을 테스트 할 수없는 경우 이미 수행 한 것처럼 try catch 블록으로 코드를 둘러싸 야합니다 (예상되는 특정 오류를 포착하는 것이 좋습니다).


오류 조건 테스트는 일반적으로 예외가 비싸기 때문에 일반적으로 좋은 방법입니다.
Dirk Vollmar

"방어 프로그래밍"은 시대에 뒤 떨어진 패러다임입니다. 모든 오류 조건에 대한 테스트 결과로 생성 된 비대해진 코드는 결국 해결하는 것보다 더 많은 문제를 야기합니다. TDD 및 처리 예외는 현대적인 접근 방식 IMHO
Joe Soul-bringer

@Joe-모든 오류 조건에 대한 테스트에 동의하지 않지만 때로는 예외를 피하는 간단한 검사 비용과 예외 자체의 차이를 고려할 때 특히 의미가 있습니다.
Ken Henderson

1
-1 여기서 resource.Close ()는 예외를 던질 수 있습니다. 추가 리소스를 닫아야하는 경우 예외로 인해 함수가 반환되고 열린 상태로 유지됩니다. 이것이 OP에서 두 번째 try / catch의 목적입니다.
Outlaw Programmer

@Outlaw-Close가 예외를 throw하고 리소스가 열려 있으면 예외를 캡처하고 억제하여 문제를 해결하는 방법은 무엇입니까? 따라서 나는 그것이 전파되도록 두었습니다 (아직 열려있는 상태에서 회복 할 수있는 것은 매우 드뭅니다).
Ken Henderson

0

이것을 다른 방법으로 리팩토링 할 수 있습니다.

public void RealDoSuff()
{
   try
   { DoStuff(); }
   catch
   { // resource.close failed or something really weird is going on 
     // like an OutOfMemoryException 
   }
}

private void DoStuff() 
{
  try 
  {}
  catch
  {
  }
  finally 
  {
    if (resource != null) 
    {
      resource.close(); 
    }
  }
}

0

나는 보통 이것을한다 :

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}

이론적 근거 : 자원을 다 썼고 내가 가진 유일한 문제는 그것을 닫는 것이라면 내가 할 수있는 일이 많지 않다. 어쨌든 리소스를 다 사용했다면 전체 스레드를 죽이는 것도 말이되지 않습니다.

이것은 적어도 나를 위해 선택된 예외를 무시하는 것이 안전한 경우 중 하나입니다.

오늘날까지이 관용구를 사용하는 데 아무런 문제가 없었습니다.


나중에 누수를 발견 할 경우를 대비하여 기록하겠습니다. 그렇게하면 그들이 어디에서 왔는지 (아님) 알 수 있습니다
Egwor

@Egwor. 동의합니다. 이것은 단지 빠른 스미 펫일뿐입니다. 내가 너무하고 probaly 캐치를 사용하여 로그인 뭔가 :)을 제외 할 수 있습니다
OscarRyz

0
try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

완료되었습니다. null 테스트가 없습니다. 단일 catch, 획득 및 해제 예외 포함. 물론 Execute Around 관용구를 사용할 수 있으며 각 리소스 유형에 대해 한 번만 작성하면됩니다.


5
use (resource)가 Exception A를 던지고 resource.release ()가 예외 B를 던지면 어떻게 될까요? 예외 A 손실 ...
Darron

0

변경 Resource에서 가장 좋은 대답Closeable

스트림 구현 Closeable따라서 모든 스트림에 대해 메소드를 재사용 할 수 있습니다.

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}

0

리소스로 try를 사용할 수없는 비슷한 상황이 발생했지만 closeQuietly 메커니즘처럼 로그하고 무시하는 것이 아니라 닫기에서 오는 예외를 처리하고 싶었습니다. 제 경우에는 실제로 출력 스트림을 처리하지 않으므로 닫기 실패가 단순한 스트림보다 더 중요합니다.

IOException ioException = null;
try {
  outputStream.write("Something");
  outputStream.flush();
} catch (IOException e) {
  throw new ExportException("Unable to write to response stream", e);
}
finally {
  try {
    outputStream.close();
  } catch (IOException e) {
    ioException = e;
  }
}
if (ioException != null) {
  throw new ExportException("Unable to close outputstream", ioException);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.