Throwable을 잡는 것이 나쁜 습관입니까?


답변:


104

가능한 한 구체적이어야합니다. 그렇지 않으면 예상치 못한 버그가 이런 식으로 사라질 수 있습니다.

게다가, Throwable커버 Error도 마찬가지이며 일반적으로 반환 지점이 아닙니다 . 당신은 그것을 잡거나 다루고 싶지 않고, 당신은 당신의 프로그램이 즉시 죽어 올바르게 고칠 수 있기를 원합니다.


38
오류를 포착하고 계속하는 것이 적절한 상황이 있습니다. 예 : 서블릿에서 특정 요청이 모든 메모리를 차지하기 때문에 OutOfMemoryError를 enconter하면 요청이 처리 된 후 객체가 GC가되므로 계속할 수 있습니다. 어설 션 오류도 마찬가지입니다. 요청에 문제가있어 애플리케이션을 종료하지 않습니다.
gawi apr

7
할당 된 항목과 OOME 이전에 할당되지 않은 항목을 어떻게 알 수 있습니까? Tomcat 또는 JBoss와 같은 J2EE 컨테이너 내부에서도이를 얻으면 모든 베팅이 해제됩니다.
bmauter 2013 년

10
우리는 NoSuchMethodError를 겪었고 고맙게도 배포 2 주 후 발생한 서버를 종료하여 모든 고객을 제거하지 않았습니다. 즉. 우리는 항상 Throwable을 포착하고 오류를 처리하고 고객에게 보내기 위해 최선을 다하고 있습니다. 결국 1000 명의 고객 중 1 명에게만 영향을 미칠 수 있다는 점에서 복구 가능한 오류 유형이 많이 있습니다.
Dean Hiller 2015 년

10
" 프로그램이 즉시 종료되어 제대로 고칠 수 있기를 원합니다. "=> 프로그램이 종료되면 무슨 일이 일어 났는지 어떻게 알 수 있습니까? 문제를 기록하는 Throwable / 오류를 잡기 할 수있는 합리적인 일이 ...
assylias

3
@assylias 독립 실행 형 응용 프로그램은 stderr에 치명적인 오류를 발생시킵니다.
Philip Whitehouse

36

이것은 나쁜 생각입니다. 사실, 잡는 Exception것 조차도 일반적으로 나쁜 생각입니다. 예를 들어 보겠습니다.

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

이제 getUserInput ()이 잠시 차단되고 다른 스레드가 최악의 방법으로 스레드를 중지한다고 가정 해 보겠습니다 (thread.stop () 호출). catch 블록은 ThreadDeath오류 를 포착합니다 . 이것은 매우 나쁩니다. 해당 예외를 포착 한 후 코드의 동작은 거의 정의되지 않았습니다.

예외를 잡는 것과 비슷한 문제가 발생합니다. 아마도getUserInput() 때문에 InterruptException의 실패, 또는 결과, 또는 다른 실패의 모든 종류를 기록하는 동안 권한은 예외를 부인했다. 그 때문에 문제를 해결하는 방법도 모르기 때문에 무엇이 잘못되었는지 전혀 모릅니다.

세 가지 더 나은 옵션이 있습니다.

1-처리 방법을 알고있는 예외를 정확히 포착합니다.

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2-발생하고 처리 방법을 모르는 예외를 다시 발생시킵니다.

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3-finally 블록을 사용하면 다시 던지는 것을 기억할 필요가 없습니다.

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }

4
예외를 잡는 것조차 좋지 않다는 +1. 적어도 특별한주의를 필요로하는 ThreadInterruptedException가 (짧은 - 그것을 잡는 후, 당신은 '사실'로 설정 thread의 인터럽트 상태의 뒷면에 있습니다)
키릴 Gamazkov

나는 그것이 당신의 단어를 설명하기위한 것임을 알고 있지만 사용자 입력이 영숫자인지 또는 필요한 형식인지 항상 정규식으로 확인할 수 있으며 매번 try catch를 사용하지 마십시오.
amdev

오류 / Throwable이 있는지 파악하지 못하면 어떻게 알 수 있습니까? 로그에서 아무것도 보지 못했습니다. Java EE 앱. 이 캐치를 추가 할 때까지 문제가 무엇인지 모르고 며칠을 보냈습니다.
Philip Rego

2
나는 옵션 # 2를 나쁜 습관으로 생각합니다. 로그 및 다시 던짐이있는 10 개의 체인 통화를 상상해보십시오. 로그 파일을 보면 만족스럽지 않습니다. 예외는 10 번 기록되므로 로그를 읽기가 매우 어렵습니다. IMHO는 훨씬 더 나은 것 throw new Exception("Some additional info, eg. userId " + userId, e);입니다. 이것은 10 가지 원인과 함께 하나의 멋진 예외에 기록됩니다.
Petr Újezdský

21

또한을 잡을 때 특별한 치료가 필요한 Throwable잡을 수도 있습니다 InterruptedException. 자세한 내용 은 InterruptedException 처리 를 참조하십시오.

확인되지 않은 예외 만 포착하려는 경우이 패턴을 고려할 수도 있습니다.

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

이렇게하면 코드를 수정하고 확인 된 예외를 throw 할 수있는 메서드 호출을 추가 할 때 컴파일러가이를 상기시켜주고이 경우에 수행 할 작업을 결정할 수 있습니다.


14

Error 클래스의 javadoc에서 직접 가져온 것입니다 (잡지 않는 것이 좋습니다).

 * An <code>Error</code> is a subclass of <code>Throwable</code> 
 * that indicates serious problems that a reasonable application 
 * should not try to catch. Most such errors are abnormal conditions. 
 * The <code>ThreadDeath</code> error, though a "normal" condition,
 * is also a subclass of <code>Error</code> because most applications
 * should not try to catch it. 

 * A method is not required to declare in its <code>throws</code> 
 * clause any subclasses of <code>Error</code> that might be thrown 
 * during the execution of the method but not caught, since these 
 * errors are abnormal conditions that should never occur. 
 *
 * @author  Frank Yellin
 * @version %I%, %G%
 * @see     java.lang.ThreadDeath
 * @since   JDK1.0

13

메서드에서 예외 버블을 절대로 가질 수 없다면 나쁜 습관이 아닙니다.

정말 예외를 처리 할 수 ​​없다면 나쁜 습관입니다. 단순히 catch하고 다시 던지는 것보다 메소드 서명에 "throws"를 추가하는 것이 더 좋으며, 더 나쁜 것은 RuntimeException에 래핑하고 다시 던지는 것입니다.


10
전적으로 동의합니다. 모든 Throwable인스턴스 를 처리하는 데 절대적으로 합법적 인 경우가 있습니다 ( 예 : 사용자 정의 예외 로깅).
Yuriy Nakonechnyy

9

오류를 지나치게 많이 던지는 라이브러리를 사용하는 경우 Throwable 잡기가 필요할 때가 있습니다. 그렇지 않으면 라이브러리가 애플리케이션을 종료 할 수 있습니다.

그러나 이러한 상황에서는 모든 Throwables가 아니라 라이브러리에서 발생한 특정 오류 만 지정하는 것이 가장 좋습니다.


12
아니면 더 나은 서면 라이브러리를 사용 하시겠습니까?
Raedwald

6
사실, 당신은 선택 ;-)이있는 경우
DNA

던질 수있는 물건을 잡고 다시 던지는 것이 가장 큰 문제입니다. 스택의 업스트림에있는 모든 메서드에 대해 불가능한 인터페이스를 만듭니다. 그들은 던질 수있는 것을 처리해야하거나 다른 사람들이 처리해야하는 사용할 수없는 던질 수있는 서명을 가지고 있어야합니다.
Andrew Norman

6

Throwable은 throw 될 수있는 모든 클래스의 기본 클래스입니다 (예외뿐만 아니라). OutOfMemoryError 또는 KernelError를 포착하면 할 수있는 일이 거의 없습니다 ( java.lang.Error 를 포착하는 경우 참조 )

예외를 잡는 것으로 충분합니다.


5

그것은 귀하의 논리 또는 귀하의 옵션 / 가능성에 더 구체적으로 달려 있습니다. 의미있는 방식으로 대응할 수있는 특정 예외가있는 경우 먼저 포착하여 그렇게 할 수 있습니다.

없는 경우 모든 예외 및 오류에 대해 동일한 작업을 수행 할 것이라고 확신하는 경우 (예 : 오류 메시지로 종료) throwable을 잡는 것은 문제가되지 않습니다.

일반적으로 첫 번째 케이스는 유효하며 던질 수있는 것을 잡을 수 없습니다. 그러나 여전히 잘 잡히는 경우가 많이 있습니다.


4

매우 나쁜 습관으로 설명되지만 때때로 드물게 발견 될 수 있습니다. 되지만 유용 할뿐만 아니라 필수 인 경우도 있습니다. 다음은 두 가지 예입니다.

사용자에게 의미있는 전체 오류 페이지를 표시해야하는 웹 애플리케이션에서. 이 코드는 try/catch모든 요청 처리기 (서블릿, struts 작업 또는 모든 컨트롤러 ....) 주변 에서 큰 문제가 발생하는지 확인합니다 .

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

또 다른 예로, 자금 이체 비즈니스에 서비스를 제공하는 서비스 클래스가 있다고 가정하십시오. 이 메서드는 TransferReceipt전송이 완료되었거나 NULL불가능한 경우를 반환합니다 .

String FoundtransferService.doTransfer( fundtransferVO);

이제 이미징 List은 사용자로부터 자금 이체를 받고 위의 서비스를 사용하여 모든 작업을 수행해야합니다.

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

하지만 무슨 일이 일어날 어떤 예외가됩니까? 한 번의 전송이 성공하고 다른 하나는 성공하지 않았을 수 있으므로 중단해서는 안됩니다. 모든 사용자를 계속 진행 List하고 각 전송에 결과를 표시해야합니다. 따라서이 코드로 끝납니다.

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

많은 오픈 소스 프로젝트를 탐색하여 throwable실제로 캐시되고 처리되는지 확인할 수 있습니다. 예를 들어 여기의 검색이며 tomcat, struts2그리고 primefaces:

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable


1
이 링크에서 코드를 보았습니다. 던질 수있는 것은 잡히는 것만이 아닙니다! Throwable 이전에 잡힌 다른 예외도 있습니다.
developer1011

@ developer101 당연히,하지만 그들은 잡습니다 throwable.
이것이이

4

질문은 약간 모호합니다. " Throwable잡아도 Throwable됩니까"아니면 " 잡아도 아무 것도하지 않아도 괜찮 습니까?"라고 물으십니까? 여기에있는 많은 사람들이 후자에 대해 대답했지만 그것은 부수적 인 문제입니다. 당신이 잡고있다 여부를 시간의 99 %는 당신이해야하지, "소비"또는 예외를 폐기 Throwable하거나 IOException또는 무엇 이건.

예외를 전파하면 많은 질문에 대한 대답과 같은 대답은 "상황에 따라 다름"입니다. 예외를 제외하고 수행하는 작업에 따라 다릅니다.이를 포착하는 이유는 무엇입니까?

잡으려 Throwable는 이유에 대한 좋은 예는 오류가있는 경우 일종의 정리를 제공하는 것입니다. 예를 들어 JDBC에서 트랜잭션 중에 오류가 발생하면 트랜잭션을 롤백 할 수 있습니다.

try {
  
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

예외는 삭제되지 않고 전파됩니다.

그러나 일반적인 정책으로, Throwable이유가없고 어떤 특정 예외가 발생하는지 알기에는 너무 게으 르기 때문에 잡는 것은 잘못된 형식이며 나쁜 생각입니다.


1

일반적으로 Errors를 잡는 것을 피하고 싶지만 그렇게하는 것이 적절한 경우 (적어도) 두 가지 특정 경우를 생각할 수 있습니다.

  • 특히 AssertionError 무해한 오류에 대한 응답으로 애플리케이션을 종료하려고합니다 .
  • ExecutorService.submit () 와 유사한 스레드 풀링 메커니즘을 구현하고 있습니까? 예외를 처리 할 수 ​​있도록 사용자에게 다시 전달해야합니다.

0

throwable 을 사용하면 Error 도 다룹니다 .

예.

    public class ExceptionTest {
/**
 * @param args
 */
public static void m1() {
    int i = 10;
    int j = 0;
    try {
        int k = i / j;
        System.out.println(k);
    } catch (Throwable th) {
        th.printStackTrace();
    }
}

public static void main(String[] args) {
    m1();
}

}

산출:

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)

0

Throwable은 모든 오류와 예외의 슈퍼 클래스입니다. catch 절에서 Throwable을 사용하면 모든 예외를 포착 할뿐만 아니라 모든 오류도 포착합니다. JVM은 애플리케이션에서 처리 할 수없는 심각한 문제를 나타 내기 위해 오류를 발생시킵니다. 이에 대한 일반적인 예는 OutOfMemoryError 또는 StackOverflowError입니다. 둘 다 응용 프로그램의 제어를 벗어난 상황으로 인해 발생하며 처리 할 수 ​​없습니다. 따라서 Throwable 내부에있는 예외 일 뿐이라는 확신이 없으면 Throwables를 잡아서는 안됩니다.


-1

Throwable을 잡는 것은 일반적으로 나쁜 습관이지만 (이 질문에 대한 수많은 답변에 의해 설명 된 바와 같이), 잡는 시나리오는 Throwable 가 유용한 는 매우 일반적입니다. 제가 직장에서 사용하는 사례 중 하나를 간단한 예를 들어 설명하겠습니다.

두 개의 숫자를 더하는 방법을 고려하고 성공적으로 더한 후 특정 사람들에게 이메일 알림을 보냅니다. 반환 된 숫자가 중요하고 호출 메서드에서 사용한다고 가정합니다.

public Integer addNumbers(Integer a, Integer b) {
    Integer c = a + b;          //This will throw a NullPointerException if either 
                                //a or b are set to a null value by the
                                //calling method
    successfulAdditionAlert(c);
    return c;
}

private void successfulAdditionAlert(Integer c) {
    try {
        //Code here to read configurations and send email alerts.
    } catch (Throwable e) {
        //Code to log any exception that occurs during email dispatch
    }
}

이메일 경고를 보내는 코드는 많은 시스템 구성을 읽으므로 해당 코드 블록에서 다양한 예외가 발생할 수 있습니다. 그러나 경고 전달 중에 발생한 예외가 호출자 메서드로 전파되는 것을 원하지 않습니다. 해당 메서드는 단순히 제공하는 두 정수 값의 합계와 관련이 있기 때문입니다. 따라서 이메일 경고를 발송하는 코드는 try-catch블록에 배치되며 , 여기서 Throwable잡히고 예외는 로깅 만되어 나머지 흐름이 계속됩니다.


이메일을 보내는 작업 (큐 포함) 전용 스레드를 사용하여이를 방지하려고합니다.
boumbh

나는 이것이 여전히 나쁜 코드라고 제안합니다. 반드시 Exceptions를 잡아라 Throwable. 그러나 .
andrewf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.