문제

저는 다음과 같은 일부 코드 문제를 해결하면서 상당히 고통스러운 문제 해결 경험을 했습니다.

try {
   doSomeStuff()
   doMore()
} finally {
   doSomeOtherStuff()
}

doSomeStuff()에서 예외가 발생하고 이로 인해 doSomeOtherStuff()에서도 예외가 발생했기 때문에 문제를 해결하기가 어려웠습니다.두 번째 예외(finally 블록에서 발생)는 내 코드에 발생했지만 문제의 실제 근본 원인인 첫 번째 예외(doSomeStuff()에서 발생)에 대한 핸들이 없었습니다.

코드가 대신 이렇게 말했다면 문제는 쉽게 드러났을 것입니다.

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

그래서 제 질문은 이렇습니다.

catch 블록 없이 사용되는 finally 블록은 잘 알려진 Java 안티 패턴입니까?(확실히 잘 알려진 안티 패턴인 "예외를 삼키지 마세요!"의 쉽게 나타나지 않는 하위 클래스인 것 같습니다.)

도움이 되었습니까?

해결책

일반적으로 아니요, 이것은 안티 패턴이 아닙니다.finally 블록의 요점은 예외 발생 여부에 관계없이 항목이 정리되도록 하는 것입니다.예외 처리의 요점은 처리할 수 없는 경우 상대적으로 깔끔한 대역 외 신호 예외 처리를 통해 처리할 수 있는 사람에게 문제를 알리도록 하는 것입니다.예외가 발생한 경우 항목을 정리해야 하지만 현재 범위에서 예외를 적절하게 처리할 수 없는 경우 이것이 바로 올바른 작업입니다.finally 블록이 던지지 않도록 하는 데 좀 더 주의를 기울이는 것이 좋습니다.

다른 팁

나는 여기의 진짜 "반포 팬"이 finally 캐치가없고 던질 수있는 블록.

전혀.

잘못된 것은 마침내 내부의 코드입니다.

마침내 항상 처형 될 것이며, 방금 목격 한 것처럼 예외를 던질 수있는 무언가를 넣을 위험이 있습니다.

마침내 그리고 캐치가없는 시도는 전혀 잘못된 것이 없습니다. 다음을 고려하세요:

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

예외가 발생 하고이 코드가이를 처리하는 방법을 알지 못하면 예외는 호출 스택을 발신자에게 기포해야합니다. 이 경우 우리는 여전히 스트림을 정리하고 싶어서 캐치없이 시도 블록을 갖는 것이 완벽하다고 생각합니다.

나는 그것이 반란 방지와는 거리가 멀고, 메소드 실행 중에 얻은 자원을 거래하는 것이 중요 할 때 매우 자주하는 일이라고 생각합니다.

파일 핸들 (쓰기 용)을 처리 할 때 내가하는 한 가지는 스트림을 플러시하는 것입니다.


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

나는 다음과 같은 이유로 그렇게하는 것을 좋아합니다.

  • 파일을 닫을 때 예외를 무시하는 것은 완전히 안전하지 않습니다. 아직 파일에 기록되지 않은 바이트가있는 경우 파일이 발신자가 기대할 상태에 있지 않을 수 있습니다.
  • 따라서 Flush () 메소드 중에 예외가 발생하면 발신자에게 전파되지만 모든 파일이 닫혀 있는지 확인합니다. ioutils.closequietly (...) 방법은 덜 말이 덜 동시에 해당 시도 ... 캐치 ... 나를 무시합니다.
  • 여러 출력을 사용하는 경우 Flush () 메소드의 순서가 중요합니다. 다른 스트림을 생성자로 전달하여 생성 된 스트림을 먼저 플러시해야합니다. Close () 메소드에는 동일한 것이 유효하지만 Flush ()는 내 의견으로는 더 명확합니다.

캐치 블록이없는 시도 블록은 패턴 방지라고 말하고 싶습니다. "캐치가없는 마지막으로 없다"고 말하는 것은 "캐치없이 시도하지 마십시오"의 하위 집합입니다.

시도/마지막으로 다음 형식으로 사용합니다.

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

나는 이것을 try/catch/finall (마침내+ 중첩 시도/캐치)보다 선호합니다. 나는 그것이 더 간결하다고 생각하고 캐치 (예외)를 복제하지 않습니다.

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

그렇게하지 마십시오 ... 당신은 더 많은 버그를 숨겼습니다 (정확히 숨기지 마십시오 ...하지만 처리하기가 더 어려워졌습니다. 예외를 포착 할 때 NullPointer 및 ArrayindExoutOfBounds와 같은 모든 종류의 runtimeexception도 잡을 것입니다. .

일반적으로 잡아야 할 예외 (확인 된 예외)를 잡고 테스트 시간에 다른 사람들을 처리하십시오. runtimeexeceptions는 프로그래머 오류에 사용되도록 설계되었으며 프로그래머 오류는 올바르게 디버깅 된 프로그램에서는 일어나지 말아야 할 사항입니다.

제 생각에는 finally a catch 어떤 종류의 문제를 나타냅니다. 리소스 관용구는 매우 간단합니다.

acquire
try {
    use
} finally {
    release
}

Java에서는 거의 모든 곳에서 예외를 가질 수 있습니다. 획득은 종종 점검 된 예외를 던집니다. 끔찍한 귀무인 검사를 시도하지 마십시오.

당신이 실제로 항문이 되려면 예외 중에 우선 순위가 암시된다는 점에 유의해야합니다. 예를 들어 Threaddeath는 획득/사용/릴리스에서 온 것인지에 관계없이 모든 것을 막아야합니다. 이러한 우선 순위를 올바르게 처리하는 것은보기 흉한 것입니다.

따라서, 자원을 관용구 주변의 실행과 함께 추상화하십시오.

시도/마침내 자원이 적절하게 무료로 제공되는 방법입니다. 최종 블록의 코드는 시도 블록에 입력하기 전에 획득 한 자원이나 상태에만 작용해야하므로 절대 던지지 않아야합니다.

제쳐두고, 나는 log4J가 생각합니다 거의 반포 팬.

실행 프로그램을 검사하려면 적절한 검사 도구 (예 : 디버거, IDE 또는 극단적으로 바이트 코드 직물을 사용하지만 몇 줄에 로깅 진술을하지 마십시오!).

두 가지 예에서는 첫 번째 예를 제시합니다. 두 번째는 로거 코드를 포함하고 버그를 소개합니다. 두 번째 예에서는 첫 두 문장에 의해 던져지면 예외를 억제합니다 (예 : 잡아서 그것을 기록하지만 다시 로그 로그인하지 않습니다. 이것은 log4J 사용에서 매우 일반적이며 응용 프로그램 설계의 실제 문제입니다. 기본적으로. 당신의 변화를 통해 당신은 예외가없는 것처럼 기본적으로 행진하기 때문에 시스템이 처리하기가 매우 어려운 방식으로 프로그램을 실패하게합니다 (VB Basic on Error 이력서 다음 구성).

try-finally 메소드에 여러 가지가있는 경우 복사-정서 코드를 줄이는 데 도움이 될 수 있습니다. return 진술. 다음 예제 (Android Java)를 고려하십시오.

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) {
    Cursor cursor = db.rawQuery("SELECT * FROM table", null);
    if (cursor != null) { 
        try {
            if (cursor.getCount() == 0) { 
                return false;
            }
        } finally {
            // this will get executed even if return was executed above
            cursor.close();
        }
    }
    // database had rows, so do something...
    return true;
}

없다면 finally 절, 당신은 써야 할 수도 있습니다 cursor.close() 두 번 : 직전에 return false 그리고 주변 이후 if 절.

나는 캐치가없는 시도가 반 패턴이라고 생각합니다. 시도/캐치를 사용하여 처리합니다 특별한 조건 (파일 IO 오류, 소켓 타임 아웃 등)은 패턴 방지가 아닙니다.

청소를 위해 시도/마지막으로 사용하는 경우 대신 사용 블록을 고려하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top