English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Java에서 예외 처리를 효과적으로 처리하는 세 가지 원칙

예외가 강력한 디버깅 도구인 이유는 그것이 다음 세 가지 질문에 답변하기 때문입니다:

     1어떤 오류가 발생했는지?63;

     2어디서 오류가 발생했는지?63;

     3어떻게 오류가 발생했는지?63;

효과적으로 예외를 사용할 때, 예외 유형은 '무엇'이 투출되었는지에 답변하며, 예외 스택 트래킹은 '어디서' 투출되었는지에 답변하며, 예외 정보는 '왜' 투출되었는지에 답변합니다.如果你的异常没有回答以上全部问题,那么可能你没有很好地使用它们。

디버깅 과정에서 예외를 최대한 활용하는 데 도움이 되는 세 가지 원칙이 있습니다:

     1구체적이고 명확하게

     2착수

     3지연된 캡처

효과적인 예외 처리의 이 세 가지 원칙을 설명하기 위해, 이 글은 허구의 개인 재정 관리자 클래스 JCheckbook를 통해 논의합니다. JCheckbook는 예금, 출금, 증권 발행 등의 은행 계정 활동을 기록하고 추적하는 데 사용됩니다.

구체적이고 명확하게

Java는 Throwable에서 시작되는 예외 클래스의 계층 구조를 정의하고 있으며, Error와 Exception을 확장하며, Exception은 RuntimeException을 확장합니다. 그림으로 보면:1.

이 네 가지 클래스는 일반화되어 있으며, 많은 오류 정보를 제공하지 않습니다. 하지만 이러한 클래스를 인스턴스화하는 것은 문법적으로 유효합니다(예: new Throwable()), 그러나 가상 기반 클래스로 사용하는 것이 더 좋습니다. Java는 많은 예외 서브클래스를 제공하며, 더 구체적이고자 할 경우, 자신의 예외 클래스를 정의할 수 있습니다.

예를 들어: java.io 패키지는 Exception 클래스의 서브클래스인 IOException를 정의하고 있으며, 더 특화된 것으로는 FileNotFoundException, EOFException, ObjectStreamException과 같은 IOException 서브클래스가 있습니다. 각각은 특정 I를 설명합니다./O 오류: 파일이 손실되었음, 이상한 파일 끝과 잘못된 시리얼라이즈된 객체 스트림입니다. 예외가 더 구체적일수록, 우리의 프로그램은 '어떤 문제가 발생했는지'에 대한 답변을 더 잘 할 수 있습니다.

이상한 캡처가 발생했을 때 명확하게 처리하는 것은 매우 중요합니다. 예를 들어: JCheckbook는 FileNotFoundException을 처리하기 위해 사용자에게 파일 이름을 다시 질문할 수 있으며, EOFException의 경우, 예외가 발생하기 전에 읽은 정보를 기반으로 계속 실행할 수 있습니다. ObjectStreamException이 발생하면, 프로그램은 사용자에게 파일이 손상되었음을 알리고, 백업 파일이나 다른 파일을 사용해야 합니다.

Java는 명확한 예외 잡기를 쉽게 만들어줍니다. 우리는 동일한 try 블록에 여러 catch 블록을 정의할 수 있으므로, 각 예외에 대해 적절한 처리를 할 수 있습니다.

File prefsFile = new File(prefsFilename);
try{
  readPreferences(prefsFile);
}
catch (FileNotFoundException e){
  // 사용자에게 지정된 파일
  // 없습니다
}
catch (EOFException e){
  // 사용자에게 파일의 끝에 도달했음을 알림
  // 에 도달했습니다
}
catch (ObjectStreamException e){
   // 사용자에게 파일이 손상되었음을 알림
}
catch (IOException e){
  // 사용자에게 다른 I를 알림/O
  // 에러 발생
}

JCheckbook은 사용자에게 예외를 잡은 명확한 정보를 제공하기 위해 여러 catch 블록을 사용합니다. 예를 들어, FileNotFoundException이 잡혔을 때, 사용자에게 다른 파일을 지정하도록 알릴 수 있습니다. 여러 catch 블록이 추가적인 코드 작업을 유발할 수 있는 경우, 추가적인 코드 작업은 불필요한 부담일 수 있습니다. 하지만 이 예제에서 추가적인 코드는 프로그램이 사용자에게 더 친절한 응답을 제공하는 데 도움이 됩니다.

처음 세 개의 catch 블록에서 처리된 예외 이외에, 마지막 catch 블록은 IOException이 발생할 때 사용자에게 더 일반적인 오류 정보를 제공합니다. 이렇게 하면 프로그램이 가능한 한 구체적인 정보를 제공할 수 있지만, 예상치 못한 다른 예외를 처리할 수 있는 능력도 있습니다.

때때로 개발자들은 범화된 예외를 잡고, 예외 클래스 이름을 표시하거나 스택 정보를 출력하여 '정확하게' 하려고 합니다. 그렇지 마세요! 사용자가 java.io.EOFException이나 스택 정보를 보면, 도움을 받는 대신 두통을 피할 수 없습니다. 구체적인 예외를 잡고, 사용자에게 정확한 정보를 '인간의 언어'로 알려주어야 합니다. 그러나 예외 스택은 로그 파일에 출력할 수 있습니다. 기억하세요, 예외와 스택 정보는 개발자에게 도움을 주기 위한 것이며, 사용자에게는 아닙니다.

결국, JCheckbook은 readPreferences()에서 예외를 잡지 않고, 예외를 잡고 처리하는 것을 사용자 인터페이스 층으로 두어, 다이얼로그나 다른 방식으로 사용자에게 알릴 수 있습니다. 이를 '지연 잡기'라고 합니다. 다음에서 설명할 것입니다.

빠른 예외 발생

이 예외 스택 정보는 예외가 발생한 메서드 호출 시퀀스의 정확한 순서를 제공하며, 각 메서드 호출의 클래스 이름, 메서드 이름, 코드 파일 이름, 심지어 행 번호까지 포함하여 예외가 발생한 현장을 정확히 위치시킵니다.

java.lang.NullPointerException
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:103)
at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:225)
at jcheckbook.JCheckbook.startup(JCheckbook.java:116)
at jcheckbook.JCheckbook.<init>(JCheckbook.java:27)
at jcheckbook.JCheckbook.main(JCheckbook.java:318)

위는 FileInputStream 클래스의 open() 메서드가 NullPointerException을 표현하는 예입니다. 그러나 주의해야 할 것은 FileInputStream.close()이 표준 Java 클래스 라이브러리의 일부이며, 이 예외를 일으킬 수 있는 원인이 우리의 코드가 아니라 Java API가 아니라는 것입니다. 따라서 문제는 가능성이 높게 이전의 어떤 메서드에서 발생했으며, 행운스럽게도 스택 정보에도 출력되었습니다.

불운히NullPointerException는 Java에서 가장 정보가 적은(하지만 가장 자주 발생하고 심각한) 예외입니다. 이 예외는 우리가 가장 중요하게 생각하는 것을 전혀 언급하지 않습니다: null이 어디에 있는지. 따라서 몇 단계를 되돌아가서 어디서 잘못되었는지 찾아야 합니다.

스택 정보를 차근차근 되돌아가며 추적하고 코드를 확인하면, readPreferences()에 빈 파일 이름 파라미터가 전달되었음을 확인할 수 있습니다. readPreferences()는 빈 파일 이름을 처리할 수 없음을 알고 있으므로 즉시 해당 조건을 확인합니다:

public void readPreferences(String filename)
throws IllegalArgumentException{
  if (filename == null){
     throw new IllegalArgumentException("filename is null");
  } //if
  //...기타 작업을 수행합니다...
  InputStream in = new FileInputStream(filename);
  //...예를 들어, 설정 파일을 읽습니다...
}

이른 시간에 예외를 발생시키면(또는 '빠른 실패'라고도 합니다), 예외가 명확하고 정확하게 처리됩니다. 스택 정보는 즉시 무엇이 잘못되었는지(잘못된 파라미터 값을 제공하였음), 왜 잘못되었는지(파일 이름이 비어 있을 수 없음), 그리고 어디서 잘못되었는지(readPreferences()의 초기 부분)을 즉시 반영합니다. 따라서 우리의 스택 정보는 사실에 맞게 제공됩니다:

java.lang.IllegalArgumentException: 파일 이름이 null입니다
at jcheckbook.JCheckbook.readPreferences(JCheckbook.java:207)
at jcheckbook.JCheckbook.startup(JCheckbook.java:116)
at jcheckbook.JCheckbook.<init>(JCheckbook.java:27)
at jcheckbook.JCheckbook.main(JCheckbook.java:318)

또한, 포함된 예외 정보("파일 이름이 비어 있습니다")는 이전 코드에서 푸시한 NullPointerException보다 더 풍부한 정보를 제공하여, 이전 코드에서 제공할 수 없는 무엇이 비어 있는지 명확히 답합니다.

오류를 감지했을 때 즉시 예외를 푸시하여 빠른 실패를 이루어, 불필요한 객체 생성이나 자원 사용(예: 파일이나 네트워크 연결)을 피할 수 있습니다. 또한, 이러한 자원을 열 때 발생하는 클리어 작업도 제거할 수 있습니다.

지연 캡처

불명확한 사용자와 고수가 잘하는 잘못 중 하나는 예외를 처리할 수 있는 상태가 아니라도 예외를 캡처하는 것입니다. Java 컴파일러는 검출된 예외가 반드시 캡처되거나 푸시되어야 한다고 요구함으로써 이러한 행동을 간접적으로 장려합니다. 자연스럽게, 코드를 try 블록으로 감싸고 catch를 사용하여 예외를 캡처하여 컴파일러 오류를 피하는 것이 일반적입니다.

문제는 예외를 캡처한 후에는 어떻게 하느냐는 것입니다. 가장 좋지 않은 방법은 아무것도 하지 않는 것입니다. 비어 있는 catch 블록은 모든 예외를 영원히 잃게 만드는 것과 같으며, 어떤 때, 어디서, 왜 오류가 발생했는지에 대한 모든 정보가 영원히 잃어버립니다. 예외를 로그에 기록하는 것은 조금은 나아지만, 최소한 기록이 남습니다. 하지만 사용자가 로그 파일과 예외 정보를 읽거나 이해할 수는 없습니다. readPreferences()가 오류 정보 대화 상자를 표시하는 것도 적절하지 않습니다. 왜냐하면 JCheckbook이 현재 데스크톱 애플리케이션임에도 불구하고, 웹 기반 웹 애플리케이션으로 변할 계획이 있기 때문입니다. 그런 경우, 오류 대화 상자를 표시하는 것은 명백히 선택이 아닙니다. 또한, HTML이든 C든/S 버전에서는 설정 정보는 서버에서 읽고, 오류 정보는 웹 브라우저나 클라이언트 프로그램에 표시되어야 합니다. readPreferences()는 설계 시 이러한 미래의 요구사항도 고려해야 합니다. 사용자 인터페이스 코드와 프로그램 로직을 적절히 분리하여 코드의 재사용성을 높일 수 있습니다.

예외를 처리하기 전에 너무 일찍 캡처하면 보통 더 심각한 오류와 다른 예외가 발생합니다. 예를 들어, 이전의 readPreferences() 메서드가 FileInputStream 생성자를 호출할 때FileNotFoundException이 발생할 가능성을 즉시 캡처하고 기록하면 다음과 같은 코드가 됩니다:

public void readPreferences(String filename){
  //...
  InputStream in = null;
  // 이를하지 마세요!!!
try{
  in = new FileInputStream(filename);
}
catch (FileNotFoundException e){
  logger.log(e);
}
in.read(...);
//...
}

위의 코드는 FileNotFoundException에서 복구할 수 없는 상황에서 그것을 캡처합니다. 파일이 찾을 수 없다면, 다음 메서드는 그 파일을 읽을 수 없습니다. readPreferences()가 존재하지 않는 파일을 읽도록 요청받으면 어떻게 될까요? 물론, FileNotFoundException이 기록될 것입니다. 우리가 로그 파일을 확인하면 알 수 있습니다. 그러나 프로그램이 파일에서 데이터를 읽으려고 시도할 때 무엇이 일어날까요? 파일이 없기 때문에 변수 in은 비어 있으며, NullPointerException이 발생합니다.

프로그램을 디버깅할 때, 자연스럽게 로그의 마지막 정보를 확인하려는 경향이 있습니다. 그것은 NullPointerException이며, 매우 불쾌한 것입니다. 이 예외는 매우 구체적이지 않기 때문에. 오류 메시지는 무엇이 잘못되었는지(정확한 오류는 FileNotFoundException이 아니라 NullPointerException)를 혼동시키고, 오류의 원인을 혼동시킵니다. 실제 문제는 NullPointerException을 표시한 수행문보다 수행문이 여러 번 호출되고 클래스가 소멸하는 중에 있습니다. 우리의 주의는 이 작은 물고기로부터 진정한 오류에서 벗어나서 로그를 되돌아보고 문제의 원인을 발견할 때까지 끌려갑니다.

그렇다면 readPreferences()가 실제로 해야 할 일이 이러한 예외를 캡처하는 것이 아니라면 무엇일까요? 일반적인 상식에 반대되는 것처럼 보이지만, 실제로 가장 적절한 방법은 아무것도 하지 않고 즉시 예외를 캡처하지 않는 것입니다. 책임을 readPreferences() 호출자에게 맡겨서 설정 파일이 누락되었음을 처리하는 적절한 방법을 탐구하도록 합니다. 그는 사용자에게 다른 파일을 지정하도록 권장하거나, 불가피하게는 사용자에게 경고하고 프로그램을 종료할 수 있습니다.

코드에서 예외 처리 책임을 호출 체인의 상위로 전달하는 방법은 메서드의 throws 절에 예외를 선언하는 것입니다. 가능한 한 구체적인 예외를 선언할 때 주의하세요. 이는 호출하는 코드가 알고 처리할 준비가 되어 있는 예외 유형을 식별하는 데 사용됩니다. 예를 들어, "지연된 캡처" 버전의 readPreferences()는 다음과 같을 수 있습니다:

public void readPreferences(String filename)
throws IllegalArgumentException,
FileNotFoundException, IOException{
  if (filename == null){
      throw new IllegalArgumentException("filename is null");
   } //if
   //...
   InputStream in = new FileInputStream(filename);
//...
}

기술적으로 우리가 선언해야 할 유일한 예외는 IOException입니다. 우리는 FileNotFoundException이 메서드에서 발생할 수 있다는 것을 명확히 선언했습니다. IllegalArgumentException은 필수 선언이 아닙니다. 이는 비 검사 예외(즉 RuntimeException의 서브 클래스)이기 때문입니다. 그러나 이를 선언하는 것은 코드의 문서화를 위해입니다. (이 예외들도 메서드의 JavaDocs에 표시되어야 합니다).

물론, 최종적으로 프로그램은 예외를 잡아야 하며, 그렇지 않으면 예기치 않게 종료됩니다. 하지만 이 기술은 적절한 수준에서 예외를 잡아 프로그램이 예외에서 의미 있게 복구하고 계속 진행할 수 있도록 하거나, 사용자에게 명확한 정보를 제공하여 사용자가 오류에서 복구할 수 있도록 하는 것입니다.如果你的方法无法胜任,那么就不要处理异常,把它留到后面捕获和在恰当的层面处理。

결론

경험 많은 개발자들은 프로그램의 최대 어려움은 결함을 수리하는 것이 아니라, 엄청난 코드 중에서 결함의 숨은 곳을 찾는 것이라는 것을 알고 있습니다. 이 글의 세 가지 원칙을 따르면, 이상이 프로그램이 결함을 추적하고 제거하여 더 강력하고 사용자에게 친화적인 프로그램으로 만들어질 수 있습니다. 이 글은 이제 끝이며, 여러분의 학습이나 업무에 도움이 되길 바랍니다.

언급: 본 문서의 내용은 인터넷에서 수집되었으며, 저작권은 원저자에게 있으며, 인터넷 사용자가 자발적으로 기여하고 업로드한 내용입니다. 이 사이트는 소유권을 가지지 않으며, 인공적인 편집을 하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해가 의심되는 내용이 있으시면, 이메일로 notice#w에 보내 주시기 바랍니다.3codebox.com에 대한 신고를 위해 이메일을 보내시면, #을 @으로 변경하시고 관련 증거를 제공하시면 됩니다. 실제로 확인되면, 이 사이트는 즉시 저작권 침해 내용을 제거합니다.

추천해드립니다