Java의 예외, Part 2 : 고급 기능 및 유형

JDK 1.0 은 예상되는 프로그램 동작과 다른 예외 처리를위한 언어 기능 및 라이브러리 유형 프레임 워크를 도입했습니다 . 이 튜토리얼의 전반부에서는 Java의 기본 예외 처리 기능에 대해 설명했습니다. 이 후반부에서는 JDK 1.0과 그 후속 제품인 JDK 1.4, JDK 7, JDK 9에서 제공하는 고급 기능을 소개합니다. 스택 추적, 원인 및 예외 체이닝과 같은 고급 기능을 사용하여 Java 프로그램에서 예외를 예상하고 관리하는 방법을 알아보십시오. -자원, 멀티 캐치, 최종 재 투입 및 스택 워킹.

이 자습서의 코드 예제는 JDK 12와 호환됩니다.

다운로드 코드 받기이 자습서에서 예제 응용 프로그램의 소스 코드를 다운로드합니다. JavaWorld를 위해 Jeff Friesen이 만들었습니다.

JDK 1.0 및 1.4의 예외 처리 : 스택 추적

각 JVM 스레드 (실행 경로)는 스레드가 생성 될 때 생성 되는 스택 과 연결됩니다 . 이 데이터 구조는 메서드 호출과 관련된 데이터 구조 인 프레임 으로 나뉩니다 . 이러한 이유로 각 스레드의 스택을 종종 메서드 호출 스택이라고 합니다.

메서드가 호출 될 때마다 새 프레임이 생성됩니다. 각 프레임은 지역 변수, 매개 변수 변수 (메서드에 전달 된 인수를 포함), 호출 메서드로 반환하기위한 정보, 반환 값을 저장하기위한 공간, 예외를 전달하는 데 유용한 정보 등을 저장합니다.

스택 트레이스 (또한으로 알려진 스택 백 트레이스는 ) 스레드의 실행 중에 특정 시점에서 활성 스택 프레임의 보고서입니다. 패키지 의 Java Throwable클래스 java.lang는 스택 추적을 인쇄하고 스택 추적을 채우고 스택 추적의 요소에 액세스하는 메소드를 제공합니다.

스택 추적 인쇄

throw문 적합한하는 던질 그것을 제 모습을 발생 catch실행중인 블록 방법. 발견되지 않으면 catch예외를 처리 할 수 있는 가장 가까운 블록을 찾는 메서드 호출 스택을 해제합니다 . 찾을 수없는 경우 JVM이 적절한 메시지와 함께 종료됩니다. 목록 1을 고려하십시오.

목록 1. PrintStackTraceDemo.java(버전 1)

import java.io.IOException; public class PrintStackTraceDemo { public static void main(String[] args) throws IOException { throw new IOException(); } }

목록 1의 인위적인 예제는 java.io.IOException객체 를 생성 하고이 객체를 main()메소드 밖으로 던집니다 . 있으므로 main()던질 처리하지 않고 있기 때문에 main()최상위에있어서, 적절한 메시지를 갖는 JVM이 종료된다. 이 응용 프로그램의 경우 다음 메시지가 표시됩니다.

Exception in thread "main" java.io.IOException at PrintStackTraceDemo.main(PrintStackTraceDemo.java:7)

JVM은 호출 메시지 출력 Throwablevoid printStackTrace()호출에 대한 스택 트레이스 인쇄 방법에있어서, Throwable표준 오차 스트림 오브젝트. 첫 번째 줄은 throwable의 toString()메서드 를 호출 한 결과를 보여줍니다 . 다음 줄은 이전에 기록한 데이터를 보여줍니다 fillInStackTrace()(잠시 설명).

추가 인쇄 스택 추적 방법

Throwable의 오버로드 void printStackTrace(PrintStream ps)void printStackTrace(PrintWriter pw)메서드는 지정된 스트림 또는 작성기에 스택 추적을 출력합니다.

스택 추적은 throwable이 생성 된 소스 파일과 줄 번호를 보여줍니다. 이 경우 PrintStackTrace.java소스 파일의 7 번째 줄에 생성되었습니다 .

printStackTrace()일반적으로 catch블록 에서 직접 호출 할 수 있습니다 . 예를 들어 PrintStackTraceDemo애플리케이션 의 두 번째 버전을 고려하십시오 .

목록 2. PrintStackTraceDemo.java(버전 2)

import java.io.IOException; public class PrintStackTraceDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { ioe.printStackTrace(); } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

목록 2 것은 계시 main()메소드를 호출 방법 a()메소드를 호출 b(). 메소드 b()IOExceptionJVM에 객체를 던져 예외를 처리 할 수있는 main()catch블록을 찾을 때까지 메소드 호출 스택을 해제 합니다. 예외는 printStackTrace()throwable 을 호출 하여 처리됩니다 . 이 메서드는 다음 출력을 생성합니다.

java.io.IOException at PrintStackTraceDemo.b(PrintStackTraceDemo.java:24) at PrintStackTraceDemo.a(PrintStackTraceDemo.java:19) at PrintStackTraceDemo.main(PrintStackTraceDemo.java:9)

printStackTrace()스레드의 이름을 출력하지 않습니다. 대신 toString()throwable에서 호출 java.io.IOException하여 첫 번째 줄에 출력되는 throwable의 정규화 된 클래스 이름 ( ) 을 반환합니다 . 그런 다음 메서드 호출 계층을 출력합니다. 가장 최근에 호출 된 메서드 ( b())는 맨 위에 있고 main()맨 아래에 있습니다.

스택 추적은 어떤 줄을 식별합니까?

스택 추적은 throwable이 생성되는 라인을 식별합니다. throwthrowable이 생성 된 동일한 줄에서 throw되지 않는 한 throwable이 throw되는 줄 (을 통해 )을 식별하지 않습니다 .

스택 추적 채우기

ThrowableThrowable fillInStackTrace()실행 스택 추적을 채우는 메서드를 선언합니다 . 호출 Throwable객체에서 현재 스레드 스택 프레임의 현재 상태에 대한 정보를 기록합니다. 목록 3을 고려하십시오.

목록 3. FillInStackTraceDemo.java(버전 1)

import java.io.IOException; public class FillInStackTraceDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { ioe.printStackTrace(); System.out.println(); throw (IOException) ioe.fillInStackTrace(); } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

목록 3과 목록 2의 주요 차이점은 catch블록의 throw (IOException) ioe.fillInStackTrace();설명입니다. 이 문은 ioe의 스택 추적을 대체 한 후 throwable이 다시 던져집니다. 다음 출력을 관찰해야합니다.

java.io.IOException at FillInStackTraceDemo.b(FillInStackTraceDemo.java:26) at FillInStackTraceDemo.a(FillInStackTraceDemo.java:21) at FillInStackTraceDemo.main(FillInStackTraceDemo.java:9) Exception in thread "main" java.io.IOException at FillInStackTraceDemo.main(FillInStackTraceDemo.java:15)

IOException객체가 생성 된 위치를 식별하는 초기 스택 추적을 반복하는 대신 두 번째 스택 추적은의 위치를 ​​나타냅니다 ioe.fillInStackTrace().

Throwable 생성자 및 fillInStackTrace()

의 각 Throwable생성자는 fillInStackTrace(). 그러나 (JDK 7에 도입) 다음 생성자는이 메서드 Invoke 당신은 통과하지 않을 때 false합니다 writableStackTrace:

Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)

fillInStackTrace()스택 추적을 빌드하기 위해 현재 스레드의 메서드 호출 스택을 따라가는 네이티브 메서드를 호출합니다. 이 걷기는 비용이 많이 들고 너무 자주 발생하면 성능에 영향을 미칠 수 있습니다.

성능이 중요한 상황 (임베디드 장치 관련)에 처한 경우를 재정 의하여 스택 추적이 빌드되지 않도록 할 수 있습니다 fillInStackTrace(). 목록 4를 확인하십시오.

목록 4. FillInStackTraceDemo.java(버전 2)

{ public static void main(String[] args) throws NoStackTraceException { try { a(); } catch (NoStackTraceException nste) { nste.printStackTrace(); } } static void a() throws NoStackTraceException { b(); } static void b() throws NoStackTraceException { throw new NoStackTraceException(); } } class NoStackTraceException extends Exception { @Override public synchronized Throwable fillInStackTrace() { return this; } }

Listing 4 introduces NoStackTraceException. This custom checked exception class overrides fillInStackTrace() to return this -- a reference to the invoking Throwable. This program generates the following output:

NoStackTraceException

Comment out the overriding fillInStackTrace() method and you'll observe the following output:

NoStackTraceException at FillInStackTraceDemo.b(FillInStackTraceDemo.java:22) at FillInStackTraceDemo.a(FillInStackTraceDemo.java:17) at FillInStackTraceDemo.main(FillInStackTraceDemo.java:7)

Accessing a stack trace's elements

At times you'll need to access a stack trace's elements in order to extract details required for logging, identifying the source of a resource leak, and other purposes. The printStackTrace() and fillInStackTrace() methods don't support this task, but JDK 1.4 introduced java.lang.StackTraceElement and its methods for this purpose.

The java.lang.StackTraceElement class describes an element representing a stack frame in a stack trace. Its methods can be used to return the fully-qualified name of the class containing the execution point represented by this stack trace element along with other useful information. Here are the main methods:

  • String getClassName() returns the fully-qualified name of the class containing the execution point represented by this stack trace element.
  • String getFileName() returns the name of the source file containing the execution point represented by this stack trace element.
  • int getLineNumber() returns the line number of the source line containing the execution point represented by this stack trace element.
  • String getMethodName() returns the name of the method containing the execution point represented by this stack trace element.
  • boolean isNativeMethod() returns true when the method containing the execution point represented by this stack trace element is a native method.

JDK 1.4 also introduced the StackTraceElement[] getStackTrace() method to the java.lang.Thread and Throwable classes. This method respectively returns an array of stack trace elements representing the invoking thread's stack dump and provides programmatic access to the stack trace information printed by printStackTrace().

Listing 5 demonstrates StackTraceElement and getStackTrace().

Listing 5. StackTraceElementDemo.java (version 1)

import java.io.IOException; public class StackTraceElementDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { StackTraceElement[] stackTrace = ioe.getStackTrace(); for (int i = 0; i < stackTrace.length; i++) { System.err.println("Exception thrown from " + stackTrace[i].getMethodName() + " in class " + stackTrace[i].getClassName() + " on line " + stackTrace[i].getLineNumber() + " of file " + stackTrace[i].getFileName()); System.err.println(); } } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

When you run this application, you'll observe the following output:

Exception thrown from b in class StackTraceElementDemo on line 33 of file StackTraceElementDemo.java Exception thrown from a in class StackTraceElementDemo on line 28 of file StackTraceElementDemo.java Exception thrown from main in class StackTraceElementDemo on line 9 of file StackTraceElementDemo.java

마지막으로, JDK 1.4은 도입 setStackTrace()에 방법을 Throwable. 이 메서드는 RPC (원격 프로 시저 호출) 프레임 워크 및 기타 고급 시스템에서 사용하도록 설계되어 클라이언트가 fillInStackTrace()throwable이 생성 될 때 생성되는 기본 스택 추적을 재정의 할 수 있습니다 .

이전 fillInStackTrace()에 스택 추적이 빌드되지 않도록 재정의하는 방법을 보여주었습니다 . 대신 StackTraceElement및 을 사용하여 새 스택 추적을 설치할 수 setStackTrace()있습니다. StackTraceElement다음 생성자를 통해 초기화 된 객체 배열을 만들고이 배열을에 전달합니다 setStackTrace().

StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber)

목록 6은 StackTraceElementsetStackTrace().