Java Language
예외 및 예외 처리
수색…
소개
Throwable
형의 오브젝트 및 그 서브 타입은, throw
키워드를 try…catch
스택에 보내져 try…catch
문으로 try…catch
할 수 있습니다.
통사론
void someMethod () throws SomeException {} // 메서드 선언. SomeException가 확인 된 예외 유형 인 경우 메서드 호출자가 catch하도록합니다.
시도 {
someMethod(); //code that might throw an exception
}
catch (SomeException e) {
System.out.println("SomeException was thrown!"); //code that will run if certain exception (SomeException) is thrown
}
마지막으로 {
//code that will always run, whether try block finishes or not
}
try-catch를 사용하여 예외 잡기
try...catch
문을 사용하여 예외를 포착하고 처리 할 수 있습니다. 사실 try
문은 try...catch...finally
및 try-with-resources
대한 다른 예제에서 설명한대로 다른 형식을 취 try-with-resources
.
하나의 catch 블록이있는 try-catch
가장 간단한 형식은 다음과 같습니다.
try {
doSomething();
} catch (SomeException e) {
handle(e);
}
// next statement
간단한 try...catch
의 동작은 다음과 같습니다.
-
try
블록의 명령문이 실행됩니다. -
try
블록의 문에 의해 예외가 throw되지 않으면try...catch
후 컨트롤이 다음 문으로 넘어갑니다. -
try
블록 내에서 예외가 Throw되었을 경우.- 예외 객체는
SomeException
또는 하위 유형의 인스턴스인지 여부를 확인하기 위해 테스트됩니다. - 그렇다면
catch
블록이 예외를 catch 합니다.- 변수
e
는 예외 객체에 바인딩됩니다. -
catch
블록 내의 코드가 실행됩니다. - 이 코드가 예외를 throw하면 새로 throw 된 예외가 원래 예외 대신 전파됩니다.
- 그렇지 않으면
try...catch
후 제어가 다음 명령문으로 넘어갑니다.
- 변수
- 일치하지 않으면 원래 예외가 전파됩니다.
- 예외 객체는
여러 개의 캐치가있는 try-catch
try...catch
는 여러 개의 catch
블록을 가질 수도 있습니다. 예 :
try {
doSomething();
} catch (SomeException e) {
handleOneWay(e)
} catch (SomeOtherException e) {
handleAnotherWay(e);
}
// next statement
여러 개의 catch
블록이있는 경우, 첫 번째 블록부터 시작하여 예외에 대한 일치가 발견 될 때까지 한 번에 하나씩 시도됩니다. 해당 핸들러가 (위와 같이) 실행 된 다음 try...catch
문 다음에 다음 명령문으로 제어가 전달됩니다. 핸들러 코드가 예외를 throw하더라도 일치하는 블록 다음의 catch
블록은 항상 건너 뜁니다.
catch
블록의 예외가 분리되어 있지 않은 경우에는 "하향식"일치 전략이 효과가 있습니다. 예 :
try {
throw new RuntimeException("test");
} catch (Exception e) {
System.out.println("Exception");
} catch (RuntimeException e) {
System.out.println("RuntimeException");
}
이 코드 스 니펫은 "RuntimeException"대신 "Exception"을 출력합니다. RuntimeException
은 Exception
의 하위 유형이므로 첫 번째 (보다 일반적인) catch
가 일치합니다. 두 번째 (보다 구체적인) catch
는 실행되지 않습니다.
여기서 배울 수있는 교훈은 예외 유형과 관련하여 가장 구체적인 catch
블록이 먼저 나타나야하며 가장 일반적인 catch
블록이 가장 먼저 나타납니다. (일부 Java 컴파일러는 catch
를 실행할 수 없으면 경고하지만 컴파일 오류가 아닙니다.)
다중 예외 catch 블록
Java SE 7부터는 단일 catch
블록이 관련없는 예외 목록을 처리 할 수 있습니다. 예외 유형이 세로 막대 ( |
) 기호로 구분되어 나열됩니다. 예 :
try {
doSomething();
} catch (SomeException | SomeOtherException e) {
handleSomeException(e);
}
다중 예외 캐치의 동작은 단일 예외 상황에 대한 간단한 확장입니다. catch
예외는 throw 된 예외가 나열된 예외 중 하나 (적어도)와 일치하는 경우 일치합니다.
사양에 약간의 미묘함이 있습니다. e
유형은 목록에있는 예외 유형의 합성 결합 입니다. e
의 값이 사용될 때, 정적 유형은 유형 공용의 최소 일반 상위 유형입니다. 그러나 catch
블록 내에서 e
가 다시 throw되면 throw되는 예외 유형은 union의 유형입니다. 예 :
public void method() throws IOException, SQLException
try {
doSomething();
} catch (IOException | SQLException e) {
report(e);
throw e;
}
위의 경우, IOException
및 SQLException
은 최소 일반 상위 유형이 Exception
합니다. 즉, report
메서드는 report(Exception)
와 일치해야합니다. 그러나 컴파일러는 throw가 IOException
또는 SQLException
만 throw
할 수 있음을 알고 있습니다. 따라서 method
throws Exception
것이 아니라 throws IOException, SQLException
로 선언 될 수 있습니다. (좋은 점은 : Pitfall - Throwable Throwable, Exception, Error 또는 RuntimeException을 보라.)
예외를 던집니다.
다음 예제는 예외를 throw하는 기본 사항을 보여줍니다.
public void checkNumber(int number) throws IllegalArgumentException {
if (number < 0) {
throw new IllegalArgumentException("Number must be positive: " + number);
}
}
예외는 3 행에서 발생합니다. 이 문장은 두 부분으로 나눌 수 있습니다 :
new IllegalArgumentException(...)
은 예외가보고하는 오류를 설명하는 메시지와 함께IllegalArgumentException
클래스의 인스턴스를 작성합니다.throw ...
그런 다음 예외 객체를 던지고 있습니다.
예외가 발생하면 예외가 처리 될 때까지 둘러싸는 명령문이 비정상적 으로 종료됩니다 . 이 내용은 다른 예제에서 설명합니다.
위에 표시된 것처럼 단일 명령문에 예외 오브젝트를 작성하고 던지는 것이 좋습니다. 또한 프로그래머가 문제의 원인을 이해하는 데 도움이되는 의미있는 오류 메시지를 예외에 포함하는 것이 좋습니다. 그러나 이것이 반드시 최종 사용자에게 보여줘야하는 메시지는 아닙니다. (시작을 위해 Java는 예외 메시지를 국제화하는 데 직접적인 지원을하지 않습니다.)
몇 가지 추가 사항이 있습니다.
checkNumber
를throws IllegalArgumentException
으로 선언했습니다.IllegalArgumentException
는 검사 된 예외이므로 엄격하게 필요하지는 않습니다. Java 예외 계층 구조 - 검사되지 않은 예외 및 검사 된 예외를 참조하십시오. 그러나이를 수행하고 메소드의 javadoc 주석을 던진 예외를 포함시키는 것이 우수 실행 (good practice)입니다.throw
문 뒤에 도달 한 코드는 도달 할 수 없습니다 . 그러므로 우리가 이것을 쓴다면 :throw new IllegalArgumentException("it is bad"); return;
컴파일러는
return
문에 대해 컴파일 오류를보고합니다.
예외 연결
많은 표준 예외에는 일반적인 message
인수 외에 두 번째 cause
인수가있는 생성자가 cause
. cause
예외를 연결할 수 있습니다. 다음은 그 예입니다.
먼저 복구 할 수없는 오류가 발생하면 응용 프로그램이 던져 버릴 미 검사 예외를 정의합니다. cause
인수를 허용하는 생성자가 포함되어 cause
.
public class AppErrorException extends RuntimeException {
public AppErrorException() {
super();
}
public AppErrorException(String message) {
super(message);
}
public AppErrorException(String message, Throwable cause) {
super(message, cause);
}
}
다음은 예외 체인을 보여주는 코드입니다.
public String readFirstLine(String file) throws AppErrorException {
try (Reader r = new BufferedReader(new FileReader(file))) {
String line = r.readLine();
if (line != null) {
return line;
} else {
throw new AppErrorException("File is empty: " + file);
}
} catch (IOException ex) {
throw new AppErrorException("Cannot read file: " + file, ex);
}
}
try
블록 내의 throw
는 문제를 감지하고 간단한 메시지를 사용하여 예외를 통해보고합니다. 반대로 catch
블록 내의 throw
는 IOException
을 새로운 (확인 된) 예외로 래핑하여 처리합니다. 그러나 원래의 예외를 버리지는 않습니다. cause
IOException
을 전달하여 스택 추적 생성 및 읽기 에서 설명한대로 스택 추적에 인쇄 할 수 있도록 기록합니다.
사용자 정의 예외
대부분의 경우 Exception
를 throw 할 때 기존 일반 Exception
클래스를 사용하는 것이 코드 디자인 관점에서 더 간단합니다. 이것은 단순한 오류 메시지를 전달하기 위해 예외 만 필요로 할 경우 특히 그렇습니다. 이 경우, RuntimeException 은 보통 체크 된 Exception이 아니기 때문에 선호된다. 일반적인 오류 클래스에 대한 다른 예외 클래스가 있습니다.
- UnsupportedOperationException - 특정의 조작이 지원되어 있지 않은 경우
- IllegalArgumentException - 무효 인 파라미터 치가 메소드에 건네진 경우
- IllegalStateException - 귀하의 API가 내부적으로 발생하지 않아야하거나 귀하의 API를 무효로 사용하여 발생한 상태에 도달했습니다.
사용자 정의 예외 클래스를 사용하고자 할 경우는 다음과 같습니다 :
- 다른 사용자가 사용할 수 있도록 API 또는 라이브러리를 작성 중이며 API 사용자가 API의 예외를 특별하게 포착하고 처리 할 수 있도록 허용 하고 예외를 다른보다 일반적인 예외 와 구별 할 수 있습니다 .
- 프로그램의 다른 부분에서 잡아서 처리하려는 특정 유형의 오류 에 대한 예외를 throw하고 있으며 이러한 오류를 다른보다 일반적인 오류와 구분할 수 있기를 원합니다.
검사되지 않은 예외에 대해서는 RuntimeException
을 확장하거나 RuntimeException의 하위 클래스가 아닌 Exception
을 확장하여 Exception
를 검사하여 사용자 정의 예외를 만들 수 있습니다. 이유는 다음과 같습니다.
RuntimeException의 서브 클래스도 아닌 Exception의 서브 클래스는 예외를 확인합니다.
public class StringTooLongException extends RuntimeException {
// Exceptions can have methods and fields like other classes
// those can be useful to communicate information to pieces of code catching
// such an exception
public final String value;
public final int maximumLength;
public StringTooLongException(String value, int maximumLength){
super(String.format("String exceeds maximum Length of %s: %s", maximumLength, value));
this.value = value;
this.maximumLength = maximumLength;
}
}
사전 정의 된 예외와 같이 사용할 수 있습니다.
void validateString(String value){
if (value.length() > 30){
throw new StringTooLongException(value, 30);
}
}
예외가 잡히고 처리되는 필드를 사용할 수 있습니다.
void anotherMethod(String value){
try {
validateString(value);
} catch(StringTooLongException e){
System.out.println("The string '" + e.value +
"' was longer than the max of " + e.maximumLength );
}
}
Oracle의 Java 설명서 에 따르면 다음 사항을 염두에 두십시오.
[...] 클라이언트가 예외로부터 복구 할 것으로 합리적으로 예상 할 수 있으면 체크 예외로 만듭니다. 클라이언트가 예외로부터 복구하기 위해 아무것도 할 수 없으면 검사되지 않은 예외로 만드십시오.
더:
리소스 사용 시도 문
try-catch-final 문 예제에서 볼 수 있듯이 finally
절을 사용하는 리소스 정리에서는 가장자리 사례를 올바르게 구현하기 위해 상당한 양의 "보일러 플레이트"코드가 필요합니다. Java 7은 try-with-resources 문 형식으로이 문제를 처리하는 훨씬 간단한 방법을 제공합니다.
자원이란 무엇입니까?
Java 7은 try-with-resources 문을 사용하여 클래스를 관리 할 수 있도록 java.lang.AutoCloseable
인터페이스를 도입했습니다. AutoCloseable
을 구현하는 클래스의 인스턴스를 리소스 라고 합니다 . 이것들은 전형적으로 가비지 수집기에 의존하지 않고 적시에 처분 할 필요가 있습니다.
AutoCloseable
인터페이스는 단일 메서드를 정의합니다.
public void close() throws Exception
close()
메소드는 리소스를 적절한 방식으로 처리해야합니다. 이 명세서는 이미 폐기 된 자원에 대해 메소드를 호출하는 것이 안전해야 함을 명시합니다. 또한 Autocloseable
을 구현하는 클래스는 close()
메서드를 선언하여 Exception
보다 자세한 예외를 throw하거나 예외를 throw하지 않도록 강력히 권장 합니다.
다양한 표준 Java 클래스 및 인터페이스는 AutoCloseable
구현합니다. 여기에는 다음이 포함됩니다.
-
InputStream
,OutputStream
및 그 서브 클래스 -
Reader
,Writer
및 하위 클래스 -
Socket
및ServerSocket
및 그 서브 클래스 -
Channel
및 해당 하위 클래스 - JDBC 인터페이스
Connection
,Statement
및ResultSet
및 해당 하위 클래스.
신청서와 제 3 자 수업도이 작업을 수행 할 수 있습니다.
기본적인 try-with-resource 문
try-with-resources 구문은 고전적인 try-catch , try-finally 및 try-catch-finally 양식을 기반으로 합니다. 다음은 "기본"형식의 예입니다. 즉 catch
없는 형태 또는 finally
.
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
관리 할 리소스는 try
절 다음에 (...)
섹션에 변수로 선언됩니다. 위의 예제에서 우리는 리소스 변수 stream
선언하고 새로 생성 된 PrintStream
초기화합니다.
리소스 변수가 초기화되면 try
블록이 실행됩니다. 완료되면 stream.close()
가 자동으로 호출되어 리소스가 누출되지 않도록합니다. close()
호출은 블록이 어떻게 완료 되더라도 발생합니다.
향상된 try-with-resource 문
try-catch-finally 구문과 마찬가지로 try-with-resources 문은 catch
및 finally
블록을 사용하여 향상시킬 수 있습니다. 다음 코드 스 니펫은 PrintStream
생성자가 throw 할 수있는 FileNotFoundException
을 처리하기 위해 이전 블록에 catch
블록을 추가합니다.
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
} catch (FileNotFoundException ex) {
System.err.println("Cannot open the file");
} finally {
System.err.println("All done");
}
리소스 초기화 또는 try 블록이 예외를 throw하면 catch
블록이 실행됩니다. finally
블록은 일반적인 try-catch-finally 문과 같이 항상 실행됩니다.
몇 가지 유의할 점이 있습니다.
-
catch
및finally
블록에서 리소스 변수가 범위 를 벗어납니다. - 명령문이
catch
블록과 일치하기 전에 자원 정리가 수행됩니다. - 자동 리소스 정리가 예외를 throw하면
catch
블록 중 하나에서catch
될 수 있습니다.
여러 리소스 관리
위 코드 스 니펫은 관리중인 단일 리소스를 보여줍니다. 사실, 자원 을 사용하면 자원 한 개를 하나의 명령문으로 관리 할 수 있습니다. 예 :
try (InputStream is = new FileInputStream(file1);
OutputStream os = new FileOutputStream(file2)) {
// Copy 'is' to 'os'
}
이것은 예상했던대로 동작합니다. try
블록의 끝에서 is
와 os
는 모두 자동으로 닫힙니다. 몇 가지주의해야 할 점이 있습니다.
- 초기화는 코드 순서대로 이루어지며 나중에 리소스 변수 초기화 기는 이전 값의 값을 사용할 수 있습니다.
- 성공적으로 초기화 된 모든 자원 변수는 정리됩니다.
- 자원 변수는 선언의 역순 으로 정리됩니다.
따라서, 상기 예에서, is
전에 초기화 os
와 그 뒤에 정리하고 is
초기화 중에 예외가 있으면 청소한다 os
.
try-with-resource와 classic try-catch-finally의 동등성
Java 언어 사양은 try-catch-finally 문과 관련 하여 try-with-resource 양식의 동작을 지정합니다. (자세한 내용은 JLS를 참조하십시오.)
예를 들어, 다음과 같은 기본 try-with-resource :
try (PrintStream stream = new PrintStream("hello.txt")) {
stream.println("Hello world!");
}
try-catch-finally 와 동일하게 정의됩니다.
// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");
// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;
// The actual try block
try {
stream.println("Hello world!");
} catch (Throwable t) {
// If an exception is thrown, remember it for the finally block
primaryException = t;
throw t;
} finally {
if (primaryException == null) {
// If no exception was thrown so far, exceptions thrown in close() will
// not be caught and therefore be passed on to the enclosing code.
stream.close();
} else {
// If an exception has already been thrown, any exception thrown in
// close() will be suppressed as it is likely to be related to the
// previous exception. The suppressed exception can be retrieved
// using primaryException.getSuppressed().
try {
stream.close();
} catch (Throwable suppressedException) {
primaryException.addSuppressed(suppressedException);
}
}
}
(JLS에서는, 실제의 t
및 primaryException
변수가 통상의 Java 코드에서는 비 표시가되도록 ( primaryException
지정하고 있습니다.
리소스 가있는 try-with-resources 의 향상된 형식은 기본 형식과 동등한 것으로 지정됩니다. 예 :
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}
다음과 같습니다.
try {
try (PrintStream stream = new PrintStream(fileName)) {
stream.println("Hello world!");
}
} catch (NullPointerException ex) {
System.err.println("Null filename");
} finally {
System.err.println("All done");
}
스택 트레이스 생성 및 읽기
예외 객체가 생성 될 때 (즉 new
만들 때) Throwable
생성자는 예외가 생성 된 컨텍스트에 대한 정보를 캡처합니다. 나중에이 정보를 스택 추적 형식으로 출력 할 수 있습니다.이 스택 추적은 처음에 예외를 일으킨 문제를 진단하는 데 사용할 수 있습니다.
스택 추적 인쇄
stacktrace를 인쇄하는 것은 printStackTrace()
메서드를 호출하기 만하면됩니다. 예 :
try {
int a = 0;
int b = 0;
int c = a / b;
} catch (ArithmeticException ex) {
// This prints the stacktrace to standard output
ex.printStackTrace();
}
인자없는 printStackTrace()
메소드는 응용 프로그램의 표준 출력으로 인쇄합니다. 즉 현재 System.out
. printStackTrace(PrintStream)
및 printStackTrace(PrintWriter)
또한 지정된 Stream
또는 Writer
인쇄되는 오버로드입니다.
노트:
스택 추적에는 예외 자체에 대한 세부 정보가 포함되어 있지 않습니다.
toString()
메서드를 사용하여 이러한 세부 정보를 가져올 수 있습니다. 예// Print exception and stacktrace System.out.println(ex); ex.printStackTrace();
스택 트레이스 인쇄는 아껴서 사용해야합니다. Pitfall - 과도하거나 부적절한 스택 트레이스를 참조하십시오. 로깅 프레임 워크를 사용하고 로깅 할 예외 오브젝트를 전달하는 것이 더 좋습니다.
스택 추적 이해하기
두 개의 파일로 된 두 개의 클래스로 구성된 다음의 간단한 프로그램을 생각해보십시오. (설명을 위해 파일 이름과 줄 번호를 표시했습니다.)
File: "Main.java"
1 public class Main {
2 public static void main(String[] args) {
3 new Test().foo();
4 }
5 }
File: "Test.java"
1 class Test {
2 public void foo() {
3 bar();
4 }
5
6 public int bar() {
7 int a = 1;
8 int b = 0;
9 return a / b;
10 }
이 파일들이 컴파일되어 실행되면 다음과 같은 결과를 얻게됩니다.
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.bar(Test.java:9)
at Test.foo(Test.java:3)
at Main.main(Main.java:3)
그것이 우리에게 무엇을 말하고 있는지를 알아 내기 위해 한 번에 한 줄씩 읽으십시오.
1 번 줄은 잡히지 않은 예외로 인해 "main"이라는 스레드가 종료되었음을 알려줍니다. 예외의 전체 이름은 java.lang.ArithmeticException
이며 예외 메시지는 "/ by 0"입니다.
이 예외에 대한 javadocs를 보면 다음과 같이 표시됩니다.
예외적 인 산술 조건이 발생했을 경우에 Throw됩니다. 예를 들어, "0으로 나누기"정수는이 클래스의 인스턴스를 발생시킵니다.
실제로, "/ by zero"라는 메시지는 예외의 원인이 어떤 코드가 무언가를 0으로 나눠 시도했다는 강한 암시입니다. 근데 뭐?
나머지 세 줄은 스택 추적입니다. 각 줄은 호출 스택에서 메서드 (또는 생성자) 호출을 나타내고 각 호출은 다음 세 가지를 알려줍니다.
- 실행되고있는 클래스 및 메서드의 이름,
- 소스 코드 파일 이름,
- 실행되고있는 명령문의 소스 코드 행 번호
스택 트레이스의 이러한 행은 현재 통화의 프레임이 맨 위에 표시됩니다. 위 예제의 맨 위 프레임은 Test.bar
메소드와 Test.java 파일의 9 행에 있습니다. 그것은 다음과 같은 라인입니다 :
return a / b;
우리가 이전 위치로 파일에 몇 줄을 보면 b
초기화되고, 것을 알 수있다 b
0 값이됩니다. 이것이 예외의 원인이라고 의심 할 여지없이 말할 수 있습니다.
우리가 더 갈 필요가 있다면, 우리는 그 스택 트레이스에서 볼 수있는 bar()
호출 된 foo()
Test.java의 라인 3에서, 그리고 foo()
에서 호출 차례로이었다 Main.main()
.
참고 : 스택 프레임의 클래스 및 메서드 이름은 클래스 및 메서드의 내부 이름입니다. 다음과 같은 비정상적인 경우를 인식해야합니다.
- 중첩 된 클래스 나 내부 클래스는 "OuterClass $ InnerClass"처럼 보입니다.
- 익명의 내부 클래스는 "OuterClass $ 1", "OuterClass $ 2"등으로 표시됩니다.
- 생성자, 인스턴스 필드 이니셜 라이저 또는 인스턴스 이니셜 라이저 블록의 코드가 실행될 때 메서드 이름은 ""이됩니다.
- 정적 필드 이니셜 라이저 또는 정적 이니셜 라이저 블록의 코드가 실행될 때 메서드 이름은 ""이됩니다.
일부 Java 버전에서는 stacktrace 서식 지정 코드가 과도한 재귀로 인해 응용 프로그램이 실패 할 때 발생할 수있는 반복 스택 프레임 시퀀스를 감지하고 제거합니다.
예외 연결 및 중첩 스택 트레이스
예외 연결은 코드 조각이 예외를 포착 한 다음 첫 번째 예외를 원인으로 전달하여 새 코드를 만들고 throw합니다. 다음은 그 예입니다.
File: Test,java
1 public class Test {
2 int foo() {
3 return 0 / 0;
4 }
5
6 public Test() {
7 try {
8 foo();
9 } catch (ArithmeticException ex) {
10 throw new RuntimeException("A bad thing happened", ex);
11 }
12 }
13
14 public static void main(String[] args) {
15 new Test();
16 }
17 }
위의 클래스가 컴파일되어 실행되면 다음 스택 추적을 얻습니다.
Exception in thread "main" java.lang.RuntimeException: A bad thing happened
at Test.<init>(Test.java:10)
at Test.main(Test.java:15)
Caused by: java.lang.ArithmeticException: / by zero
at Test.foo(Test.java:3)
at Test.<init>(Test.java:8)
... 1 more
stacktrace는 (이 경우) 응용 프로그램을 중단시키는 예외에 대한 클래스 이름, 메소드 및 호출 스택으로 시작합니다. 그 다음에 cause
예외를보고하는 "원인 :"행이옵니다. 클래스 이름과 메시지가보고 된 후 원인 예외의 스택 프레임이보고됩니다. 추적은 마지막 N 개의 프레임이 이전 예외와 동일 함을 나타내는 "... N more"로 끝납니다.
"원인 :"은 기본 예외의 cause
이 null
이 아닌 경우에만 출력에 포함됩니다. 예외는 무기한으로 연결될 수 있으며이 경우 스택 추적에 여러 "원인"추적이있을 수 있습니다.
주 : cause
메카니즘은 Java 1.4.0의 Throwable
API에서만 공개되었습니다. 그 전에는 원인을 나타내는 사용자 정의 예외 필드와 사용자 정의 printStackTrace
메소드를 사용하여 응용 프로그램에서 예외 체인을 구현해야했습니다.
스택 추적을 문자열로 캡처하기
때로는 응용 프로그램이 다른 목적으로 사용될 수 있도록 스택 추적을 Java String
으로 캡처 할 수 있어야합니다. 이것을 수행하기위한 일반적인 접근법은 메모리 내부 버퍼에 쓰고이를 printStackTrace(...)
전달하는 임시 OutputStream
또는 Writer
를 만드는 것입니다.
Apache Commons 및 Guava 라이브러리는 스택 추적을 문자열로 캡처하는 유틸리티 메소드를 제공합니다.
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)
com.google.common.base.Throwables.getStackTraceAsString(Throwable)
코드 기반에서 타사 라이브러리를 사용할 수없는 경우 다음과 같은 방법으로 작업을 수행하십시오.
/**
* Returns the string representation of the stack trace.
*
* @param throwable the throwable
* @return the string.
*/
public static String stackTraceToString(Throwable throwable) {
StringWriter stringWriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
stacktrace를 분석하려는 경우 getStackTrace()
및 getCause()
를 사용하는 것이 스택 추적을 구문 분석하는 것보다 간단합니다.
InterruptedException의 처리
InterruptedException
은 혼란스러운 짐승입니다. Thread.sleep()
과 같이 겉으로보기에는 무해한 방법으로 나타나지만 잘못 처리하면 동시 환경에서 제대로 작동하지 않는 코드가됩니다.
InterruptedException
이 잡히면 가장 근본적으로 코드가 현재 실행중인 스레드에서 Thread.interrupt()
를 호출 한 누군가를 의미합니다. "내 코드입니다! 결코 중단하지 않겠습니다! " 따라서 다음과 같이하십시오.
// Bad. Don't do this.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// disregard
}
그러나 이것은 "불가능한"사건이 발생하는 것을 처리하는 잘못된 방법입니다. 애플리케이션에 InterruptedException
이 발생하지 않는다는 것을 알고 있다면 이러한 이벤트를 프로그램의 가정을 심각하게 위반 한 것으로 간주하고 최대한 빨리 종료해야합니다.
"불가능한"인터럽트를 처리하는 올바른 방법은 다음과 같습니다.
// When nothing will interrupt your code
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new AssertionError(e);
}
이것은 두 가지 일을합니다. InterruptedException
이 처음에 throw되지 않은 것처럼 스레드의 인터럽트 상태를 먼저 복원 한 다음 응용 프로그램의 기본 invariant가 위반되었음을 나타내는 AssertionError
를 throw합니다. catch
블록에 절대 도달해서는 안되기 때문에이 코드가 실행되는 스레드를 중단하지 않는다는 것을 확실하게 알고 있다면 안전합니다.
Guava의 Uninterruptibles
클래스를 사용하면이 패턴을 단순화 할 수 있습니다. Uninterruptibles.sleepUninterruptibly()
호출하면 Uninterruptibles.sleepUninterruptibly()
, 휴면 지속 시간이 만료 할 때까지 thread의 인터럽트 상태가 무시됩니다. 그 시점에서, 나중에 InterruptedException
를 조사하고 Throw하기위한 호출로 복원됩니다. 이러한 코드를 절대 중단하지 않는다는 것을 알고 있으면 잠자기 시도를 try-catch 블록으로 감쌀 필요가 없습니다.
그러나 종종 스레드가 중단되지 않을 것이라고 보장 할 수는 없습니다. 특히, Executor
또는 다른 스레드 관리가 실행할 코드를 작성하는 경우 코드가 인터럽트에 즉시 응답해야합니다. 그렇지 않으면 응용 프로그램이 멈추거나 심지어 교착 상태가 발생합니다.
그런 경우 가장 좋은 방법은 일반적으로 InterruptedException
이 호출 스택을 전파하도록 허용하는 것입니다.이 메소드 throws InterruptedException
을 차례대로 각 메소드에 전달합니다. 이것은 kludgy로 보일지 모르나 실제로는 바람직한 속성입니다. 메소드의 서명은 이제 호출자에게 인터럽트에 즉시 응답 할 것임을 나타냅니다.
// Let the caller determine how to handle the interrupt if you're unsure
public void myLongRunningMethod() throws InterruptedException {
...
}
제한된 경우 (예 : 검사 된 예외를 throw
하지 않는 메서드를 재정의하는 동안) 예외를 발생시키지 않고 중단 된 상태를 다시 설정할 수 있습니다. 인터럽트를 처리하기 위해 다음에 실행되는 코드가 무엇이든 기대합니다. 이렇게하면 인터럽트 처리가 지연되지만 완전히 중단되지는 않습니다.
// Suppresses the exception but resets the interrupted state letting later code
// detect the interrupt and handle it properly.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return ...; // your expectations are still broken at this point - try not to do more work.
}
Java Exception Hierarchy - 확인되지 않은 예외 및 확인 된 예외
모든 Java 예외는 Exception 클래스 계층에있는 클래스의 인스턴스입니다. 이것은 다음과 같이 나타낼 수 있습니다.
-
java.lang.Throwable
- 이것은 모든 예외 클래스의 기본 클래스입니다. 이 메소드 및 생성자는 모든 예외에 공통적 인 기능 범위를 구현합니다.- 예외 :
java.lang.Exception
- 모든 예외의 슈퍼 클래스입니다.- 다양한 표준 및 사용자 정의 예외 클래스.
-
java.lang.RuntimeException
- 체크되지 않는 예외 인 모든 일반 예외의 수퍼 클래스.- 다양한 표준 및 사용자 정의 런타임 예외 클래스
-
java.lang.Error
- 이것은 "치명적인 오류"예외의 수퍼 클래스입니다.
- 예외 :
노트:
- 확인하고 체크되지 않은 예외의 차이는 아래에 설명되어 있습니다.
-
Throwable
,Exception
및RuntimeException
클래스는abstract
로 취급되어야합니다. Pitfall - Throwable Throwable, Exception, Error 또는 RuntimeException를 참조하십시오. -
Error
예외는 응용 프로그램이 복구를 시도하는 것이 안전하지 않거나 현명하지 못한 상황에서 JVM에 의해 발생합니다. -
Throwable
커스텀 서브 타입을 선언하는 것은 현명하지 않을 것이다. Java 도구 및 라이브러리는Error
및Exception
가Throwable
의 유일한 직접 하위 유형이라고 가정 할 수 있으며 잘못된 가정 인 경우 오작동 할 수 있습니다.
체크 된 것과 체크되지 않은 예외
일부 프로그래밍 언어에서 예외 지원에 대한 비판 중 하나는 특정 메소드 나 프로 시저가 던질 수있는 예외를 아는 것이 어렵다는 것입니다. 처리되지 않은 예외로 인해 프로그램이 중단되는 경우가 발생하면 예외가 취약성의 원인이 될 수 있습니다.
Java 언어는 확인 된 예외 메커니즘을 통해 이러한 문제를 해결합니다. 먼저 Java는 예외를 두 가지 범주로 분류합니다.
확인 된 예외는 일반적으로 응용 프로그램이 처리 할 수 있어야하는 예상 이벤트를 나타냅니다. 예를 들어,
IOException
및 해당 부속 유형은 I / O 조작에서 발생할 수있는 오류 조건을 나타냄니다. 예를 들어 파일 또는 디렉토리가 없기 때문에 파일 열기가 실패하고 네트워크 연결이 끊어져서 네트워크 읽기 및 쓰기에 실패하는 등의 문제가 있습니다.검사되지 않은 예외는 일반적으로 응용 프로그램이 처리 할 수없는 예기치 않은 이벤트를 나타냅니다. 이는 일반적으로 응용 프로그램의 버그로 인해 발생합니다.
(다음에서 "throw 된"은 throw
문을 통해 명시 적으로 throw 된 모든 예외를 참조하거나 암시 적으로 참조 취소, 형식 변환 등에서 참조됩니다. 마찬가지로 "propagated"는 a에서 throw 된 예외를 나타냅니다. 중첩 된 호출 및 해당 호출 내에서 catch되지 않습니다. 아래 예제 코드에서는이를 보여줍니다.)
확인 된 예외 메커니즘의 두 번째 부분은 확인 된 예외가 발생할 수있는 메서드에 대한 제한이 있다는 것입니다.
- 체크 예외가하는 방법에 던져 또는 전파 될 때, 그것은 하나의 방법으로 잡은해야하며, 방법의에 나열된
throws
절을. ( 이 예 에서는throws
절의 중요성을 설명합니다.) - 확인 된 예외가 이니셜 라이저 블록에서 발생하거나 전파되면 블록을 잡아야합니다.
- 확인 된 예외는 필드 초기화 식에서 메서드 호출을 통해 전파 할 수 없습니다. 이러한 예외를 잡을 방법은 없습니다.
즉, 확인 된 예외는 처리되거나 선언되어야합니다.
이러한 제한 사항은 검사되지 않은 예외에는 적용되지 않습니다. 여기에는 모든 예외가 검사되지 않은 예외를 throw하기 때문에 예외가 암시 적으로 발생하는 모든 경우가 포함됩니다.
확인 된 예외 예제
이 코드 스 니펫은 확인 된 예외 제한을 설명하기위한 것입니다. 각각의 경우에 컴파일 오류가있는 코드 버전과 오류가 수정 된 두 번째 버전이 표시됩니다.
// This declares a custom checked exception.
public class MyException extends Exception {
// constructors omitted.
}
// This declares a custom unchecked exception.
public class MyException2 extends RuntimeException {
// constructors omitted.
}
첫 번째 예제는 명시 적으로 throw 된 확인 된 예외를 메서드에서 처리하면 안되는 경우 "throw 된"것으로 선언 될 수있는 방법을 보여줍니다.
// INCORRECT
public void methodThrowingCheckedException(boolean flag) {
int i = 1 / 0; // Compiles OK, throws ArithmeticException
if (flag) {
throw new MyException(); // Compilation error
} else {
throw new MyException2(); // Compiles OK
}
}
// CORRECTED
public void methodThrowingCheckedException(boolean flag) throws MyException {
int i = 1 / 0; // Compiles OK, throws ArithmeticException
if (flag) {
throw new MyException(); // Compilation error
} else {
throw new MyException2(); // Compiles OK
}
}
두 번째 예제는 전파 된 검사 예외를 처리하는 방법을 보여줍니다.
// INCORRECT
public void methodWithPropagatedCheckedException() {
InputStream is = new FileInputStream("someFile.txt"); // Compilation error
// FileInputStream throws IOException or a subclass if the file cannot
// be opened. IOException is a checked exception.
...
}
// CORRECTED (Version A)
public void methodWithPropagatedCheckedException() throws IOException {
InputStream is = new FileInputStream("someFile.txt");
...
}
// CORRECTED (Version B)
public void methodWithPropagatedCheckedException() {
try {
InputStream is = new FileInputStream("someFile.txt");
...
} catch (IOException ex) {
System.out.println("Cannot open file: " + ex.getMessage());
}
}
마지막 예제에서는 정적 필드 초기화 프로그램에서 확인 된 예외를 처리하는 방법을 보여줍니다.
// INCORRECT
public class Test {
private static final InputStream is =
new FileInputStream("someFile.txt"); // Compilation error
}
// CORRECTED
public class Test {
private static final InputStream is;
static {
InputStream tmp = null;
try {
tmp = new FileInputStream("someFile.txt");
} catch (IOException ex) {
System.out.println("Cannot open file: " + ex.getMessage());
}
is = tmp;
}
}
이 마지막 경우에, 우리는 또한 문제를 해결해야합니다 is
경우에도 예외의 경우, 두 번 이상 할당, 아직도에 할당해야 할 수 없습니다.
소개
예외는 프로그램이 실행 중일 때 발생하는 오류입니다. 아래 두 개의 정수를 나누는 Java 프로그램을 생각해보십시오.
class Division {
public static void main(String[] args) {
int a, b, result;
Scanner input = new Scanner(System.in);
System.out.println("Input two integers");
a = input.nextInt();
b = input.nextInt();
result = a / b;
System.out.println("Result = " + result);
}
}
이제 우리는 위의 코드를 컴파일하고 실행하고 0으로 나누기를 시도한 출력을 봅니다.
Input two integers
7 0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Division.main(Disivion.java:14)
0으로 나누기는 정수로 표시 할 수없는 값을 생성하는 잘못된 연산입니다. 자바 는 예외 를 던짐 으로써 이것을 처리한다. 이 경우 예외는 ArithmeticException 클래스의 인스턴스입니다.
주 : 스택 추적 을 작성하고 읽는 예제는 두 숫자 이후의 결과를 설명합니다.
예외 의 유틸리티는 허용되는 흐름 제어입니다. 예외를 사용하지 않고이 문제에 대한 일반적인 해결책은 먼저 b == 0
인지 확인하는 것입니다.
class Division {
public static void main(String[] args) {
int a, b, result;
Scanner input = new Scanner(System.in);
System.out.println("Input two integers");
a = input.nextInt();
b = input.nextInt();
if (b == 0) {
System.out.println("You cannot divide by zero.");
return;
}
result = a / b;
System.out.println("Result = " + result);
}
}
그러면 You cannot divide by zero.
메시지가 인쇄 You cannot divide by zero.
사용자가 0으로 나누려고하면 프로그램을 정상적으로 종료합니다. 예외 처리 를 통해이 문제를 처리하는 동일한 방법은 if
흐름 제어를 try-catch
블록으로 대체하는 것입니다.
...
a = input.nextInt();
b = input.nextInt();
try {
result = a / b;
}
catch (ArithmeticException e) {
System.out.println("An ArithmeticException occurred. Perhaps you tried to divide by zero.");
return;
}
...
try catch 블록은 다음과 같이 실행됩니다.
-
try
블록에서 코드 실행을 시작하십시오. - 예외 try 블록에서 발생하는 경우 즉시 중단하고이 예외에 의해 잡히면 확인
catch
블록 (예외의 인스턴스 인 경우,이 경우ArithmeticException
). - 예외가 포착 되면 변수
e
할당되고catch
블록이 실행됩니다. -
try
또는catch
블록이 완료되면 (즉, 코드 실행 중에 catch되지 않는 예외가 발생하지 않는 경우)try-catch
블록 아래에서 코드를 계속 실행하십시오.
일반적으로 응용 프로그램의 정상적인 흐름 제어의 일부로 예외 처리 를 사용하여 동작이 정의되지 않았거나 예기치 않게되는 것이 좋습니다. 예를 들어, 메소드가 실패 할 때 null
을 리턴하는 대신, 메소드를 사용하는 애플리케이션이 위에서 설명한 종류의 예외 처리 를 통해 상황에 대해 자체 플로우 제어를 정의 할 수 있도록 예외 를 던지는 것이 더 좋습니다. 어떤 의미에서는 특정 유형 을 반환해야하는 문제를 해결합니다. 발생한 여러 가지 예외 를 나타 내기 위해 여러 종류의 예외 중 하나가 throw 될 수 있습니다.
예외를 사용하지 않는 방법 및 사용 방법에 대한 자세한 내용은 Java Pitfalls - 예외 사용을 참조하십시오.
try catch 블록의 명령문 반환
나쁜 실행이지만 예외 처리 블록에 여러 개의 return 문을 추가 할 수 있습니다.
public static int returnTest(int number){
try{
if(number%2 == 0) throw new Exception("Exception thrown");
else return x;
}
catch(Exception e){
return 3;
}
finally{
return 7;
}
}
이 메소드는 try / catch 블록과 관련된 finally 블록이 반환되기 전에 항상 실행되기 때문에 항상 7을 반환합니다. 자, 마침내 return 7;
이 return 7;
이 값은 try / catch 반환 값보다 우선합니다.
catch 블록이 프리미티브 값을 반환하고 finally 블록에서 프리미티브 값이 변경되면 catch 블록에서 반환 된 값이 반환되고 finally 블록의 변경 사항은 무시됩니다.
아래 예제는 "1"이 아니라 "0"을 출력합니다.
public class FinallyExample {
public static void main(String[] args) {
int n = returnTest(4);
System.out.println(n);
}
public static int returnTest(int number) {
int returnNumber = 0;
try {
if (number % 2 == 0)
throw new Exception("Exception thrown");
else
return returnNumber;
} catch (Exception e) {
return returnNumber;
} finally {
returnNumber = 1;
}
}
}
예외의 고급 기능
이 예제는 몇 가지 고급 기능과 예외의 사용 사례를 다룹니다.
호출 스택을 프로그래밍 방식으로 검사
예외 스택 추적의 주요 용도는 응용 프로그램 오류 및 해당 컨텍스트에 대한 정보를 제공하여 프로그래머가 문제를 진단하고 해결할 수 있도록하는 것입니다. 때로는 다른 것들을 위해 사용될 수 있습니다. 예를 들어, SecurityManager
클래스는 호출하는 코드를 신뢰할 수 있는지 여부를 결정하기 위해 호출 스택을 검사해야 할 수 있습니다.
다음과 같이 예외를 사용하여 프로그래밍 방식으로 호출 스택을 검사 할 수 있습니다.
Exception ex = new Exception(); // this captures the call stack
StackTraceElement[] frames = ex.getStackTrace();
System.out.println("This method is " + frames[0].getMethodName());
System.out.println("Called from method " + frames[1].getMethodName());
여기에 몇 가지 중요한주의 사항이 있습니다.
StackTraceElement
에서 사용할 수있는 정보는 제한되어 있습니다.printStackTrace
가 표시하는 것보다 더 많은 정보가 없습니다. 프레임의 로컬 변수 값을 사용할 수 없습니다.getStackTrace()
대한 Javadoc은 JVM이 프레임을 빠져 나가도록 허용되어 있음을 나타냅니다.일부 가상 시스템은 경우에 따라 스택 추적에서 하나 이상의 스택 프레임을 생략 할 수 있습니다. 극단적 인 경우,이 throwable에 관한 스택 트레이스 정보를 가지지 않는 가상 머신은,이 메소드로부터 길이 제로의 배열을 돌려주는 것이 허가됩니다.
예외 구축 최적화
다른 곳에서 언급했듯이 예외를 생성하는 것은 현재 스레드의 모든 스택 프레임에 대한 정보를 캡처하고 기록하기 때문에 다소 비쌉니다. 때로는 정보가 주어진 예외에 사용되지 않을 것이라는 것을 알고 있습니다. 예 : 스택 추적이 인쇄되지 않습니다. 이 경우 정보가 캡처되지 않도록 사용자 지정 예외에서 사용할 수있는 구현 트릭이 있습니다.
스택 트레이스에 필요한 스택 프레임 정보는 Throwable
생성자가 Throwable.fillInStackTrace()
메서드를 호출 할 때 캡처됩니다. 이 메소드는 public
입니다. 즉, 서브 클래스가 그것을 오버라이드 (override) 할 수있는 것을 나타냅니다. 트릭은 Throwable
에서 상속받은 메서드를 아무 것도 수행하지 않는 메서드로 재정의하는 것입니다. 예
public class MyException extends Exception {
// constructors
@Override
public void fillInStackTrace() {
// do nothing
}
}
이 접근 방식의 문제점은 fillInStackTrace()
를 재정의하는 예외가 스택 추적을 캡처 할 수 없으며 필요 한 시나리오에서 쓸모가 없다는 것입니다.
스택 트레이 지우기 또는 바꾸기
경우에 따라 정상적인 방법으로 생성 된 예외에 대한 스택 추적에는 잘못된 정보 또는 개발자가 사용자에게 알리고 싶지 않은 정보가 포함됩니다. 이러한 시나리오의 경우 Throwable.setStackTrace
를 사용하여 정보를 보유하는 StackTraceElement
객체의 배열을 대체 할 수 있습니다.
예를 들어 다음을 사용하여 예외의 스택 정보를 삭제할 수 있습니다.
exception.setStackTrace(new StackTraceElement[0]);
금지 된 예외
Java 7은 try-with-resources 구조와 예외 억제 개념을 도입했습니다. 다음 스 니펫을 고려하십시오.
try (Writer w = new BufferedWriter(new FileWriter(someFilename))) {
// do stuff
int temp = 0 / 0; // throws an ArithmeticException
}
예외가 발생하면 try
는 w
에서 close()
를 호출하여 버퍼링 된 출력을 플러시 한 다음 FileWriter
를 닫습니다. 출력을 플러시하는 동안 IOException
이 발생하면 어떻게됩니까?
어떤 일이 발생하면 리소스를 정리하는 동안 throw 된 모든 예외가 표시되지 않습니다 . 예외가 포착되어 기본 예외의 억제 된 예외 목록에 추가됩니다. 다음으로 try-with-resources 는 다른 자원의 정리와 함께 계속됩니다. 마지막으로 기본 예외가 다시 발생합니다.
자원 초기화 중에 예외가 발생하거나 try
블록이 정상적으로 완료되면 비슷한 패턴이 발생합니다. Throw 된 첫 번째 예외가 기본 예외가되며 정리에서 발생하는 후속 예외는 표시되지 않습니다.
억제 된 예외는 getSuppressedExceptions
를 호출하여 기본 예외 객체에서 검색 할 수 있습니다.
try-finally 및 try-catch-finally 문
try...catch...finally
문은 예외 처리를 정리 코드와 결합합니다. finally
블록은 모든 상황에서 실행될 코드를 포함합니다. 따라서 리소스 관리 및 다른 종류의 정리에 적합합니다.
Try-finally
다음은 간단한 ( try...finally
) 양식의 예입니다.
try {
doSomething();
} finally {
cleanUp();
}
try...finally
의 동작은 다음과 같습니다.
-
try
블록의 코드가 실행됩니다. -
try
블록에 예외가 발생하지 않은 경우 :-
finally
블록의 코드가 실행됩니다. -
finally
블록이 예외를 throw하면 해당 예외가 전파됩니다. - 그렇지 않으면
try...finally
실행 된 후 다음 명령문으로 넘어갑니다.
-
- try 블록에서 예외가 throw 된 경우 :
-
finally
블록의 코드가 실행됩니다. -
finally
블록이 예외를 throw하면 해당 예외가 전파됩니다. - 그렇지 않으면 원래 예외가 전파됩니다.
-
finally
블록 내의 코드는 항상 실행됩니다. (유일한 예외는 System.exit(int)
가 호출되거나 JVM이 패닉하는 경우입니다. 따라서 finally
블록은 항상 실행해야하는 올바른 작업 영역 코드입니다. 예를 들어 파일 및 기타 자원을 닫거나 잠금을 해제합니다.
try-catch-finally
두 번째 예는 catch
와 finally
를 함께 사용하는 방법을 보여줍니다. 또한 자원을 정리하는 것이 쉽지 않음을 보여줍니다.
// This code snippet writes the first line of a file to a string
String result = null;
Reader reader = null;
try {
reader = new BufferedReader(new FileReader(fileName));
result = reader.readLine();
} catch (IOException ex) {
Logger.getLogger.warn("Unexpected IO error", ex); // logging the exception
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException ex) {
// ignore / discard this exception
}
}
}
이 예제에서 try...catch...finally
적인 (가상의) 동작은 여기에서 설명하기에는 너무 복잡합니다. 간단한 버전은 finally
블록의 코드가 항상 실행된다는 것입니다.
자원 관리의 관점에서 이것을 보자.
-
try
블록 앞에 "resource"(즉,reader
변수)를 선언하여finally
블록의 범위에 있도록합니다. -
new FileReader(...)
를 삽입하면catch
는 파일을 열 때 throw 된IOError
예외를 처리 할 수 있습니다. -
try
블록이나catch
블록에서 가로 챌 수없는 예외 경로가 있기 때문에finally
블록에reader.close()
가 필요합니다. - 그러나
reader
가 초기화되기 전에 예외 가 발생 했을 수 있으므로 명시 적null
테스트가 필요합니다. - 마지막으로,
reader.close()
호출은 (가설 적으로) 예외를 throw 할 수 있습니다. 우리는 그것에 대해 신경 쓰지 않지만 소스에서 예외를 잡아 내지 않으면 호출 스택을 더 처리해야합니다.
Java 7 이상에서는 자원 재 시도 를 상당히 단순화하는 try-with-resources 구문 을 제공합니다.
메소드 선언의 'throws'절
Java의 확인 된 예외 메커니즘을 사용하면 프로그래머는 특정 메서드 가 지정된 검사 예외를 throw 할 수 있음을 선언해야합니다. 이것은 throws
절을 사용하여 수행됩니다. 예 :
public class OddNumberException extends Exception { // a checked exception
}
public void checkEven(int number) throws OddNumberException {
if (number % 2 != 0) {
throw new OddNumberException();
}
}
throws OddNumberException
는 checkEven
대한 호출이 OddNumberException
유형의 예외를 throw 할 수 있다고 선언 OddNumberException
.
throws
절은 유형 목록을 선언 할 수 있으며 검사되지 않은 예외와 확인 된 예외를 포함 할 수 있습니다.
public void checkEven(Double number)
throws OddNumberException, ArithmeticException {
if (!Double.isFinite(number)) {
throw new ArithmeticException("INF or NaN");
} else if (number % 2 != 0) {
throw new OddNumberException();
}
}
체크되지 않은 예외를 던져서 선언하는 시점은 무엇입니까?
메소드 선언의 throws
절은 두 가지 용도로 사용됩니다.
컴파일러가 catch되지 않은 (확인 된) 예외를 오류로보고 할 수 있도록 예외가 발생하는 컴파일러에 알립니다.
프로그래머에게 예외를 기대하는 메소드를 호출하는 코드를 작성하는 사람에게 알려줍니다. 이러한 목적으로,
throws
리스트에 체크되지 않은 예외를 포함하는 것을 종종 감지합니다.
주 : throws
리스트는, API 문서의 생성시에 javadoc 툴이나, 일반적인 IDE의 「hover text」메소드의 힌트에 의해 사용됩니다.
Throw 및 메서드 재정의
throws
절은 메소드 재정의 목적으로 메소드의 서명의 일부를 형성합니다. 오버라이드 (override) 메소드는, 오버라이드 (override) 된 메소드 또는 서브 세트에 의해 슬로우되는 것과 같은 체크 예외 세트와 함께 선언 할 수 있습니다. 그러나 override 메서드는 추가로 검사 된 예외를 추가 할 수 없습니다. 예 :
@Override
public void checkEven(int number) throws NullPointerException // OK—NullPointerException is an unchecked exception
...
@Override
public void checkEven(Double number) throws OddNumberException // OK—identical to the superclass
...
class PrimeNumberException extends OddNumberException {}
class NonEvenNumberException extends OddNumberException {}
@Override
public void checkEven(int number) throws PrimeNumberException, NonEvenNumberException // OK—these are both subclasses
@Override
public void checkEven(Double number) throws IOExcepion // ERROR
이 규칙의 이유는 재정의 된 메서드가 throw 할 수없는 확인 된 예외를 throw 할 수있는 경우 형식 대체 가능성을 손상시킬 수 있기 때문입니다.