Java 예외는 Java가 오류를 식별하고 응답하기 위해 제공하는 일관성 메커니즘입니다.
Java 예외 메커니즘은 프로그램의 예외 처리 코드를 일반 비즈니스 코드에서 분리하고 프로그램 코드가 더 우아하고 프로그램 견고성을 향상시킬 수 있습니다. 예외를 효과적으로 사용하면 예외는 다음 세 가지 질문에 명확하게 답변 할 수 있습니다. 예외 유형은 "What"에 답변하고 예외 스택 추적 답변 "Where"및 예외 정보가 "Why"에 응답합니다.
Java 예외 메커니즘에 사용되는 몇 가지 키워드 : 시도, 잡기, 마침내 던지기, 던지기.
| 키워드 | 설명 |
|---|---|
| 노력하다 | 듣기에 사용됩니다. Try Statement 블록에 청취 할 코드 (예외를 던질 수있는 코드)를 넣으십시오. Try Statement 블록에서 예외가 발생하면 예외가 발생합니다. |
| 잡다 | 예외를 포착하는 데 사용됩니다. Catch는 Try Statement 블록에서 발생하는 예외를 포착하는 데 사용됩니다. |
| 마지막으로 | 마지막으로 명세서 블록은 항상 실행됩니다. 주로 시도 블록에서 열린 자료 리소스 (예 : 데이터베이스 연결, 네트워크 연결 및 디스크 파일)를 재활용하는 데 사용됩니다. 최종 블록을 실행 한 후에 만 시도 또는 캐치 블록에서 진술을 반환하거나 던지게됩니다. 리턴 또는 던지기와 같은 최종 문장이 사용되면 실행으로 돌아 가지 않으며 직접 중지됩니다. |
| 던지다 | 예외를 던지는 데 사용됩니다. |
| 던졌습니다 | 메소드 서명에 사용하여 방법에 의해 던질 수있는 예외를 선언합니다. |
public class demo1 {public static void main (String [] args) {try {int i = 10/0; System.out.println ( "i ="+i); } catch (arithmeticexception e) {system.out.println ( "잡힌 예외"); System.out.println ( "e.getMessage () :" + e.getMessage ()); System.out.println ( "e.tostring () :" + e.tostring ()); System.out.println ( "E.printstacktrace () :"); e.printstacktrace (); }}} 실행 결과 :
잡힌 Exceptione.getMessage () : / by zeroe.toString () : java.lang.arithmeticexception : / by zeroe.printstacktrace () : java.lang.arithmeticexception : / by demo1.main (demo1.java:6)
결과 설명 : Try Statement 블록에 0 인 Divisor가있는 작업이 있으며 작업에는 java.lang.arithmeticexception 예외가 발생합니다. 캐치에 의해 예외가 잡히는 것입니다.
결과를 관찰하면 System.out.println ( "i ="+i)이 실행되지 않은 것을 발견했습니다. 이는 Try Statement 블록에서 예외가 발생한 후 Try Statement 블록의 나머지 콘텐츠가 더 이상 실행되지 않음을 의미합니다.
예 2 : 마지막으로 기본 사용을 이해하십시오
"예 One"을 기반으로 마침내 문을 추가합니다.
public class demo2 {public static void main (String [] args) {try {int i = 10/0; System.out.println ( "i ="+i); } catch (arithmeticexception e) {system.out.println ( "잡힌 예외"); System.out.println ( "e.getMessage () :" + e.getMessage ()); System.out.println ( "e.tostring () :" + e.tostring ()); System.out.println ( "E.printstacktrace () :"); e.printstacktrace (); } 마침내 {system.out.println ( "마침내 런 마침내"); }}} 실행 결과 :
잡힌 Exceptione.getMessage () : / by zeroe.tostring () : java.lang.arithmeticexception : / by zeroe.printstacktrace () : java.lang.arithmeticexception : / by Zero at demo2.java:6).
결과 : 마지막으로 명세서 블록이 실행되었습니다.
예 3 : 던지기 및 던지기의 기본 사용법 이해
던지는 예외를 선언하는 데 사용되는 반면, 던지기는 예외를 제외하는 데 사용됩니다.
클래스 myException 확장 예외 {public myException () {} public myException (String MSG) {super (msg); }} public class demo3 {public static void main (String [] args) {try {test (); } catch (myException e) {System.out.println ( "내 예외 포획"); e.printstacktrace (); }} public static void test ()는 myException {try {int i = 10/0; System.out.println ( "i ="+i); } catch (arithmeticexception e) {Throw new MyException ( "this is myException"); }}} 실행 결과 :
내 예외 myexception을 찾으십시오 : 이것은 demo3.main에서 demo3.test (demo3.java:24)에서 myexception입니다 (demo3.java:13)
결과 : MyException은 예외에서 상속 된 서브 클래스입니다. ArithMeticeXception Exception (divider is 0)이 treat ()의 try statement 블록에서 생성되며 예외는 캐치에서 잡혔습니다. MyException 예외가 발생합니다. main () 메소드는 test ()에 던져진 myexception을 캡처합니다.
Java 예외 프레임 워크
자바 예외 아키텍처 다이어그램 :
1. 던질 수 있습니다
Throwable은 Java 언어의 모든 오류 또는 예외의 슈퍼 클래스입니다.
Throwable에는 오류 및 예외의 두 가지 서브 클래스가 포함됩니다. 그들은 일반적으로 이상이 발생했음을 나타내는 데 사용됩니다.
Throwable에는 스레드가 생성 될 때 스택을 실행하는 스레드의 스냅 샷이 포함되어 있습니다. Stack Trace 데이터와 같은 정보를 얻기 위해 PrintStacktrace ()와 같은 인터페이스를 제공합니다.
2. 예외
예외와 그 하위 클래스는 합리적인 응용 프로그램이 캡처하고자하는 조건을 지적하는 던지기 가능한 형태입니다.
3. runtimeexception
runtimeexception은 Java 가상 머신의 정상 작동 중에 예외를 제외 할 수있는 슈퍼 클래스입니다.
컴파일러는 runtimeexception 예외를 확인하지 않습니다. 예를 들어, 제수가 0 인 경우 ArithMeticexception 예외가 발생합니다. runtimeexception은 arithmeticexception의 슈퍼 클래스입니다. 코드의 제로가 0 인 경우 "Throws 선언을 통해 던져지지 않으면"또는 "시도를 통해 처리하지 않습니다 ...". 이것이 우리가 "컴파일러가 runtimeexception 예외를 확인하지 않을 것"이라고 말하는 것입니다!
코드가 runtimeexception 예외를 생성하는 경우 코드를 수정하여 피해야합니다. 예를 들어, 제수가 0 인 경우 코드를 통해이 상황을 피해야합니다!
4. 오류
예외와 마찬가지로 오류는 또한 던질 수있는 서브 클래스입니다. 합리적인 응용 프로그램이 포착하지 말아야한다는 심각한 문제를 나타내는 데 사용되며, 그러한 오류는 대부분 예외적 인 조건입니다.
runtimeexception과 마찬가지로 컴파일러는 오류를 확인하지 않습니다.
Java는 Throwable 구조를 세 가지 유형의 세 가지 유형으로 나눕니다 : 점검 예외 (점검 예외), 런타임 예외 (runtimeexception) 및 오류 (오류).
(1) 런타임 예외
정의 : runtimeexception 및 하위 클래스를 런타임 예외라고합니다.
특징 : Java 컴파일러는 확인하지 않습니다. 즉, 프로그램에서 그러한 예외가 발생할 수있을 때, "던지기 선언을 통해 던져지지 않으면"또는 "트리 캐치 문에 걸리지 않는 경우"여전히 컴파일되고 통과됩니다. 예를 들어, Divisor가 0 일 때 생성 된 ArithMeticexception 예외, 배열이 한계를 벗어 났을 때 생성 된 IndexOutoFBoundSexception 예외, 실패 메커니즘 등에 의해 생성 된 ConcurrentModificationException 예외는 모두 런타임 예외입니다.
Java 컴파일러는 런타임 예외를 확인하지 않지만, 우리는 또한 던지기를 통해 그것을 선언하고 던지거나, 시도 캐치를 통해 캡처 할 수도 있습니다.
런타임 예외가 생성되면 코드를 수정하여 피해야합니다. 예를 들어, 제수가 0 인 경우 코드를 통해이 상황을 피해야합니다!
(2) 확인 된 예외
정의 : "런타임 예외"를 제외한 예외 클래스 자체 및 기타 서브 클래스는 모두 점검 된 예외로 간주됩니다.
특징 : Java 컴파일러가 확인합니다. 이러한 예외는 트러리 캐치를 통해 던지기를 통해 선언되고 던져 지거나, 그렇지 않으면 편집 할 수 없습니다. 예를 들어 ClonenotsupportedException은 점검 된 예외입니다. 객체가 clone () 인터페이스를 통해 클로닝되고 해당 객체의 클래스가 클로닝 가능한 인터페이스를 구현하지 않으면 ClonenOnsUpportEdException이 발생됩니다.
검사되는 예외는 일반적으로 복구 될 수 있습니다.
(3) 오류
정의 : 오류 클래스 및 하위 클래스.
특징 : 런타임 예외와 마찬가지로 컴파일러는 오류를 확인하지 않습니다.
자원이 불충분하거나 제약 조건 실패 또는 다른 프로그램에서 계속 실행할 수없는 다른 조건이있을 때 오류가 발생합니다. 프로그램 자체는 이러한 오류를 해결할 수 없습니다. 예를 들어 VirtualMachineError는 오류입니다.
Java Convention에 따르면, 우리는 새로운 오류 서브 클래스를 구현해서는 안됩니다!
위의 세 가지 구조의 경우 어떤 구조가 예외 나 오류를 던져야합니까? "효과적인 Java"에 제공된 권장 사항은 다음과 같습니다. 복구 할 수있는 조건에 대한 점검 된 예외를 사용하고 프로그램 오류에 대한 런타임 예외를 사용합니다.
예외 처리에 대한 몇 가지 제안
제 1 조 : 비정상적인 상황에만 예외를 사용하십시오
권장 사항 : 예외는 비정상적인 조건에만 사용되어야하며 정상적인 제어 흐름에는 절대로 사용해서는 안됩니다.
아래 두 코드를 비교하여 설명합니다.
코드 1
{int i = 0; while (true) {arr [i] = 0; i ++; }} catch (indexOutOfBoundSexception e) {} Code 2for (int i = 0; i <arr.length; i ++) {arr [i] = 0;} 두 코드의 목적은 ARR 배열을 반복하고 배열에서 각 요소의 값을 0으로 설정하는 것입니다. 코드 1은 예외적으로 종료되며, 이는 이해하기 어려운 것처럼 보이며, 코드 2는 배열 경계에 의해 종료됩니다. 세 가지 주요 이유로 코드 1을 사용하지 않아야합니다.
예외 메커니즘의 원래 설계는 비정상적인 상황을위한 것이므로 JVM 구현은 성능을 최적화하려는 거의 없습니다. 따라서 예외를 생성, 던지기 및 잡는 오버 헤드는 비싸다.
Try-Catch Return에 코드를 넣으면 JVM이 수행해야 할 특정 특정 최적화를 구현하지 못합니다.
트래버스 어레이의 표준 패턴은 중복 검사로 이어지지 않으며 일부 최신 JVM 구현은이를 최적화합니다.
실제로 예외 기반 모드는 표준 모드보다 훨씬 느립니다. 테스트 코드는 다음과 같습니다.
공개 클래스 조언 1 {private static int [] arr = new int [] {1,2,3,4,5}; 개인 정적 int 크기 = 10000; public static void main (String [] args) {long s1 = system.currenttimeMillis (); for (int i = 0; i <size; i ++) endByRange (ARR); long e1 = system.currenttimemillis (); System.out.println ( "EndByRange 시간 :"+(e1-s1)+"ms"); long s2 = system.currenttimemillis (); for (int i = 0; i <size; i ++) endByException (ARR); Long E2 = System.CurrentTimeMillis (); System.out.println ( "EndByException Time :"+(e2-s2)+"ms"); } // ARR 배열을 가로 지르십시오 : private static void endByException (int [] arr) {try {int i = 0; while (true) {arr [i] = 0; i ++; //system.out.println("endbyRange : arr [ "+i+"] = "+arr [i]); }} catch (indexOutOfBoundSexception e) {}} // ARR 배열 전송 : private static void endByRange (int [] arr) {for (int i = 0; i <arr.length; i ++) {arr [i] = 0; //system.out.println("EndByException : arr [ "+i+"] = "+arr [i]); }}} 실행 결과 :
EndByRange 시간 : 8msendByException 시간 : 16ms
결과는 예외를 가로 지르는 속도가 평범한 방식으로 배열을 가로 지르는 것보다 훨씬 느립니다!
제 2 조 : 복구 가능한 조건에 대한 점검 된 예외를 사용하고 프로그램 오류에 대한 런타임 예외를 사용하십시오.
| 이상 | 설명 |
|---|---|
| 런타임 예외 | runtimeexception 클래스와 하위 클래스를 런타임 예외라고합니다. |
| 확인 된 예외 | "런타임 예외"를 제외한 예외 클래스 자체 및 기타 서브 클래스는 모두 확인 된 예외입니다. |
차이점은 Java 컴파일러가 "확인 된 예외"를 확인하고 "런타임 예외"를 확인하지 않는다는 것입니다.
즉, 점검 된 예외는 던지기를 통해 선언되고 던져 지거나 시도 된 시도를 통해 캡처되며, 그렇지 않으면 컴파일 할 수 없습니다. 런타임 예외에 대해서는 "던지기 선언을 통해 던져지지 않은 경우"또는 "트리 캐치 문으로 잡히지 않음"이라면 여전히 편집되어 통과됩니다. 물론 Java 컴파일러는 런타임 예외를 확인하지 않지만, 던지기를 통해 예외를 설명하거나 Try-Catch를 통해 잡을 수도 있습니다.
Rithmeticexception (예 : Divisor는 0), IndexoutoFboundsexception (예 : 바운드의 배열) 등은 모두 런타임 예외입니다. 이 예외를 위해서는 코드를 수정하여 피해야합니다. 확인 된 예외의 경우 프로그램을 복원하여 처리를 통해 진행할 수 있습니다. 예를 들어, 사용자가 충분한 수의 통화를 저장하지 않기 때문에 급여 전화로 전화를 걸려고 할 때 실패 할 것이라고 가정합니다. 따라서 점검 된 예외를 던집니다.
제 3 조 : 점검 된 예외의 불필요한 사용을 피하십시오
"검열 예외"는 Java의 좋은 기능입니다. 리턴 코드와 달리 "확인 된 예외"는 프로그래머가 예외 조건을 처리하도록하여 프로그램의 신뢰성을 크게 향상시킵니다.
그러나 점검 된 예외를 과도하게 사용하면 API가 매우 불편해질 수 있습니다. 메소드가 하나 이상의 점검 된 예외를 던지면 메소드를 호출하는 코드는 하나 이상의 캐치 명령문 블록에서 이러한 예외를 처리해야하거나 던지는 선언을 통해 던져야합니다. 캐치를 통해 처리되거나 던져진 선언을 통해 던져 지든 프로그래머에게 무시할 수없는 부담이 추가됩니다.
"확인 된 예외"에 대해서는 두 가지 조건을 충족해야합니다. 첫째, API를 올바르게 사용하더라도 예외 조건의 발생을 방해 할 수는 없습니다. 둘째, 예외가 발생하면 API를 사용하는 프로그래머는 프로그램을 처리하기 위해 유용한 조치를 취할 수 있습니다.
제 4 조 : 표준 예외를 사용하십시오
코드 재사용은 옹호에 합당하며, 이것은 일반적인 규칙이며 예외도 예외는 아닙니다. 기존 예외를 재사용하는 데 몇 가지 이점이 있습니다.
첫째, API는 프로그래머가 익숙해지는 관용구와 일치하기 때문에 API를 쉽게 배우고 사용할 수 있습니다.
둘째, 이러한 API를 사용하는 프로그램의 경우 프로그래머에게 익숙하지 않은 예외로 가득 차 있지 않기 때문에 더 잘 읽을 수 있습니다.
셋째, 예외 클래스가 적을수록 메모리 사용량이 작 으며이 클래스를 재 인쇄하는 데 소요되는 시간이 작습니다.
Java 표준 예외 중 일부는 종종 예외가 사용됩니다. 다음 표 :
| 이상 | 행사를 사용하십시오 |
|---|---|
| 불법적 인 지출 | 매개 변수 값은 적절하지 않습니다 |
| 불법 스테이트 렉싱 | 매개 변수는 부적절하지 않습니다 |
| nullPointerException | NULL이 비활성화되면 매개 변수 값이 NULL입니다 |
| indexoutofBoundSexection | 첨자는 경계를 가로 지른다 |
| 동시 대형화 소집 | 동시 수정이 금지되면 물체는 동시 수정을 감지합니다. |
| UnsupportedOperationException | 객체가 고객 요청을 지원하지 않는 방법 |
현재까지 Java 플랫폼 라이브러리의 가장 일반적으로 재사용되는 예외이지만 라이센스 조건에서 다른 예외도 재사용 할 수 있습니다. 예를 들어, 복소수 또는 행렬과 같은 산술 객체를 구현하려면 ArithMeticeXception 및 NumberFormateXception을 재사용하는 것이 매우 적절합니다. 예외가 귀하의 요구를 충족하는 경우 주저하지 말고 예외를 던지는 조건이 예외에 대한 설명서에 설명 된 조건과 일치하는지 확인해야합니다. 이 재사용은 이름이 아닌 의미론을 기반으로해야합니다!
마지막으로, 재사용 할 예외를 선택할 때 따라야 할 규칙이 없음을 분명히하십시오. 예를 들어, 카드 객체의 경우를 고려할 때 작업을 처리하는 방법이 있으며 매개 변수 (핸드 크기)가 한 손에 제공 될 카드 수라고 가정합니다. 발신자 가이 매개 변수에서 전체 데크에 대한 나머지 카드 수보다 더 큰 값을 전달한다고 가정합니다. 그런 다음이 상황은 불법적 인 지출 (핸드 크기 값이 너무 큽니다) 또는 불법 상태 외환 (카드 객체는 클라이언트의 요청에 비해 카드가 너무 적음)으로 해석 될 수 있습니다.
제 5 조 : 던진 예외는 해당 추상화에 적합해야합니다.
방법에 의해 제외 된 예외가 수행하는 작업과 명백한 상관 관계가 없다면 이러한 상황은 압도적 일 수 있습니다. 이것은 종종 방법이 저수준 추상화로 인해 발생하는 예외를 통과 할 때 발생합니다. 이런 일이 발생하면 혼란 스러울뿐만 아니라 "오염"고급 API.
이 문제를 피하기 위해 고급 구현은 낮은 수준의 예외를 포착하고 높은 수준의 추상화에 따라 도입 될 수있는 예외를 던져야합니다. 이 관행을 "예외 번역"이라고합니다.
예를 들어, Java Collection Framework Abstract Decomentiledlist get () 메소드는 다음과 같습니다 (JDK1.7.0_40 기반).
public e get (int index) {try {return listiterator (index) .next (); } catch (noSuchelementException exc) {새로운 indexOutOfBoundSexception ( "index :"+index); }}ListIterator (index)는 ListIterator 객체를 반환합니다. 객체의 다음 () 메소드를 호출하면 nosuchelementException 예외가 발생할 수 있습니다. get () 메소드에서는 nosuchelementException 예외를 던지는 것이 혼란 스러울 것입니다. 따라서 get ()는 nosuchelementexception을 캡처하고 indexoutofboundsexception 예외를 던집니다. 즉, NosuchelementException을 IndexOutOfBoundSexception 예외로 변환하는 것과 같습니다.
제 6 조 : 각 방법으로 던져진 예외는 문서화되어야합니다.
확인 된 예외를 개별적으로 선언하고 Javadoc의 @throws 태그를 사용하여 각 예외에 대한 조건을 정확하게 기록하십시오.
클래스의 많은 방법이 동일한 이유로 동일한 예외를 던지면 각 방법에 대해 개별적으로 문서화하는 대신 해당 클래스의 문서 주석 에서이 예외에 대한 문서화를 수행하는 것이 허용됩니다.
제 7 조 : 자세한 내용의 메시지 캡처 메시지를 포함시킵니다
요컨대, 예외를 사용자 정의하거나 던질 때 실패와 관련된 정보를 포함해야합니다.
잡화되지 않은 예외로 인해 프로그램이 실패하면 시스템은 예외의 스택 추적을 자동으로 인쇄합니다. 스택 트랙에 예외의 문자열 표현이 포함되어 있습니다. 일반적으로 여기에는 예외 클래스의 클래스 이름이 포함되어 있으며 다음과 같은 자세한 메시지가 포함됩니다.
제 8 조 : 실패의 원자력을 유지하기 위해 노력하십시오
객체가 예외를 던지면, 우리는 항상 객체가 잘 정의 된 상태로 유지 될 것으로 기대합니다. 발신자는 일반적으로 확인 된 예외에서 복구 될 것으로 기대하기 때문에 확인 된 예외에 특히 중요합니다.
일반적으로, 실패한 메소드 호출은 객체를 "호출되기 전에 상태"로 유지해야합니다. 이러한 속성을 가진 방법을 "실패 원자"라고합니다. 실패는 여전히 원자력을 유지한다는 것을 이해할 수 있습니다. 물체가 "실패한 원자력"을 유지하는 방법에는 여러 가지가 있습니다.
(1) 비전되지 않는 물체를 설계하십시오.
(2) 돌연변이 가능한 객체에서 작업을 수행하는 방법의 경우 "실패한 원자력"을 얻는 가장 일반적인 방법은 작업을 수행하기 전에 매개 변수의 유효성을 확인하는 것입니다. 다음과 같이 (stack.java의 팝 메소드) :
public object pop () {if (size == 0) 새 nick emptystackexception (); 객체 결과 = 요소 [-크기]; 요소 [size] = null; 반환 결과;} (3) 이전 방법과 유사하게, 계산 부분이 수정되기 전에 계산 부품의 가능한 실패가 발생하도록 계산 처리를 조정할 수있다.
(4) 작업 중 실패를 설명하고 작업이 시작되기 전에 객체를 상태로 롤백하도록 복구 코드를 작성하십시오.
(5) 오브젝트의 임시 사본에서 작업을 수행하고 작업이 완료된 후 결과를 원래 객체에 임시 사본으로 복사하십시오.
"객체의 실패한 원자력 유지"가 원하는 목표이지만 항상 가능하지는 않습니다. 예를 들어, 여러 스레드가 적절한 동기화 메커니즘없이 객체에 동시에 액세스하려고 시도하면 객체가 일관되지 않은 상태로 남을 수 있습니다.
"실패한 원자력"을 달성 할 수있는 상황에서도 항상 예상되는 것은 아닙니다. 일부 작업의 경우 오버 헤드 또는 복잡성을 크게 증가시킬 수 있습니다.
일반적인 규칙은 다음과 같습니다. 메소드 사양의 일부로 예외는 메소드를 호출하기 전에 객체 상태를 변경해서는 안됩니다. 이 규칙이 위반되면 API 문서는 객체가 어떤 상태인지 명확하게 표시해야합니다.
제 9 조 : 예외를 무시하지 마십시오
API 디자이너가 방법이 예외를 던질 것이라고 선언하면 무언가를 설명하려고합니다. 그러니 무시하지 마십시오! 예외를 무시하는 코드는 다음과 같습니다.
시도 {...} catch (somexception e) {} 빈 캐치 블록은 예외가 적당한 목적을 달성하지 못하게 만듭니다. 예외의 목적은 비정상적인 상태를 다루도록 강요하는 것입니다. 예외를 무시하는 것은 화재 경보 신호를 무시하는 것과 같습니다. 화재 경보 신호가 꺼지면 실제 화재가 발생할 때 아무도 화재 경보 신호를 볼 수 없습니다. 따라서 적어도 캐치 블록에는이 예외를 무시하는 것이 적절한 이유를 설명하는 설명이 포함되어야합니다.