Java의 예외 처리 메커니즘은 상대적으로 성숙합니다. 우리의 Java 프로그램은 예외 가능성으로 가득 차 있습니다. 이러한 예외를 미리 처리하지 않으면 나중에 프로그램 충돌을 디버깅하는 것이 불가능해집니다. 예외 위치를 찾으십시오. 이 기사에서는 Java에서 예외 및 오류를 처리하는 방법에 대해 설명합니다. 살펴보겠습니다.
예외 및 오류:
예외:
Java의 프로그램 오류는 주로 프로그램을 컴파일하고 실행할 때 발생하는 구문 오류 및 의미 오류입니다. 이를 집합적으로 예외라고 합니다. VM(가상 머신)이 이를 알리는 방법입니다. 이러한 방식으로 VM은 사용자(개발자)가 실수를 했으며 이제 이를 수정할 수 있음을 알려줍니다. 예외 클래스는 Java에서 예외를 나타내는 데 사용되며, 다른 예외 클래스는 다른 예외를 나타냅니다. 그러나 Java의 모든 예외에는 Exception이라는 기본 클래스가 있습니다.
오류:
합리적인 적용으로는 차단할 수 없는 심각한 문제를 말합니다. 대부분은 변칙입니다. 오류는 VM의 오류입니다(모든 시스템 수준 서비스일 수 있음). 따라서 오류는 처리하기 어렵고 일반 개발자(물론 귀하는 아님)는 메모리 오버플로와 같은 오류를 처리할 수 없습니다. 예외와 마찬가지로 오류 클래스는 Java에서 오류를 나타내는 데 사용되며 다른 오류 클래스는 다른 오류를 나타냅니다. 그러나 Java의 모든 오류에는 Error라는 기본 클래스가 있습니다.
요약하자면, 예외와 오류의 가장 근본적인 차이점은 예외는 개발자가 처리할 수 있는 반면, 오류는 시스템에 고유하며 일반적으로 처리할 수 없으며 프로그래머가 별도의 작업을 수행할 필요가 없다는 점입니다. 그들을 처리하십시오.
1. 예외는 프로그램 실행 중에 발생하여 정상적인 명령의 실행을 방해하는 이벤트입니다.
2. 오류, 허용되는 코드 동작을 벗어나는 동작 또는 인스턴스
예외 구조 분류:
1. 런타임 예외(unchecked 예외)
2. 컴파일 시간 예외(checked 예외)
런타임 예외는 모두 RuntimeException입니다. >
Java에서는 Exception과 Error가 공통 상위 클래스인 Throwable을 갖습니다. 오류 예외runtimeException의 여러 하위 클래스1. java.lang.ArrayIndexOutOfBoundsException
배열 인덱스 범위를 벗어난 예외입니다. 배열의 인덱스가 음수이거나 배열 크기보다 크거나 같을 때 발생합니다.
산술 조건 예외. 예: 0으로 정수 나누기 등
널 포인터 예외. 이 예외는 애플리케이션이 객체가 필요한 곳에 null을 사용하려고 시도할 때 발생합니다. 예: null 객체의 인스턴스 메서드 호출, null 객체의
속성에 액세스, null 객체의 길이 계산, null을 던지는 throw 문 사용 등
을 클래스 예외에서 찾을 수 없습니다. 애플리케이션이 문자열 형식의 클래스 이름을 기반으로 클래스를 구성하려고 시도하지만 CLASSPAH를 순회한 후 해당 이름의 클래스 파일을 찾을 수 없는 경우
이 예외가 발생합니다.
try{}catch{}finally{} finally 코드 블록은 예외 발생 여부에 관계없이 실행됩니다.
try{ }finally{}도 조합하여 사용할 수 있지만 catch{}finally{}는 사용할 수 없습니다.
public static void testException1() { int[] ints = new int[] { 1, 2, 3, 4 }; System.out.println("异常出现前"); try { System.out.println(ints[4]); System.out.println("我还有幸执行到吗");// 发生异常以后,后面的代码不能被执行 } catch (IndexOutOfBoundsException e) { System.out.println("数组越界错误"); } System.out.println("异常出现后"); } /*output:异常出现前数组越界错误常出现后*/ public static void testException2() { int[] ints = new int[] { 1, 2, 3, 4 }; System.out.println("异常出现前"); System.out.println(ints[4]); System.out.println("我还有幸执行到吗");// 发生异常以后,他后面的代码不能被执行 }
먼저 예제의 부족한 점을 지적합니다. IndexOutofBoundsException은 확인되지 않은 예외이므로 캡처를 표시하기 위해...catch...할 필요는 없지만 제 목적은 동일한 예외에 대해 다른 처리 방법을 사용하려면 어떤 차이가 있는지 확인하세요(여기서는 잠시 동안만 사용할 수 있습니다). 예외가 발생하면 첫 번째 메서드는 try 블록에서 벗어나지만 그 뒤의 코드는 계속 실행됩니다. 그러나 두 번째 종류는 다르며 더 어려운 방법에서 직접적으로 벗어납니다. 첫 번째 방법에서 우리는 try...catch...가 "트랜잭션" 보장임을 알 수 있습니다. 그 목적은 프로그램이 비정상적인 상황에서 완료되도록 하는 동시에 프로그래머에게 이를 알리는 것입니다. 프로그램 정보의 오류에 대한 세부 정보(세부 사항은 프로그래머의 설계에 따라 달라질 수 있음)
예제 2. 예외 다시 발생
public class Rethrow { public static void readFile(String file) throws FileNotFoundException { try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); System.err.println("不知道如何处理该异常或者根本不想处理它,但是不做处理又不合适,这是重新抛出异常交给上一级处理"); //重新抛出异常 throw e; } } public static void printFile(String file) { try { readFile(file); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static void main(String[] args) { printFile("D:/file"); } }
예외의 의도는 좋으니 프로그램을 고치도록 노력해 보지만 실제로는 고칠 가능성이 매우 적습니다. , 우리는 종종 오류 정보를 기록하는 데 사용됩니다. 계속해서 예외를 처리하는 데 지쳤다면 예외를 다시 발생시키는 것이 좋은 안도감을 줄 수 있습니다. 이 예외를 변경하지 않고 상위 레벨, 즉 이 메소드를 호출하는 사람에게 던지고 그것에 대해 생각하게 하십시오. 이러한 관점에서 볼 때, Java 예외(물론 확인된 예외를 의미함)는 출발점이 좋지만 우리에게 많은 문제를 야기했습니다.
예제 3. 예외 체인 사용 및 예외 손실
ExceptionA,ExceptionB,ExceptionC public class ExceptionA extends Exception { public ExceptionA(String str) { super(); } } public class ExceptionB extends ExceptionA { public ExceptionB(String str) { super(str); } } public class ExceptionC extends ExceptionA { public ExceptionC(String str) { super(str); } }
예외 손실 상황:
public class NeverCaught { static void f() throws ExceptionB{ throw new ExceptionB("exception b"); } static void g() throws ExceptionC { try { f(); } catch (ExceptionB e) { ExceptionC c = new ExceptionC("exception a"); throw c; } } public static void main(String[] args) { try { g(); } catch (ExceptionC e) { e.printStackTrace(); } } } /* exception.ExceptionC at exception.NeverCaught.g(NeverCaught.java:12) at exception.NeverCaught.main(NeverCaught.java:19) */
왜 ExceptionC만 사용하는가 는 출력되는데 ExceptionB는 출력되지 않나요?
위의 상황은 예외가 누락된 것과 동일하며 이는 문제 해결 과정에서 매우 불리합니다. 그러면 위와 같은 상황이 발생하면 어떻게 해야 할까요? 여기서 예외 체인이 시작됩니다. 즉, 예외 정보를 저장하고 원래 예외를 잃지 않고 다른 예외를 발생시킵니다.
public class NeverCaught { static void f() throws ExceptionB{ throw new ExceptionB("exception b"); } static void g() throws ExceptionC { try { f(); } catch (ExceptionB e) { ExceptionC c = new ExceptionC("exception a"); //异常连 c.initCause(e); throw c; } } public static void main(String[] args) { try { g(); } catch (ExceptionC e) { e.printStackTrace(); } } } /* exception.ExceptionC at exception.NeverCaught.g(NeverCaught.java:12) at exception.NeverCaught.main(NeverCaught.java:21) Caused by: exception.ExceptionB at exception.NeverCaught.f(NeverCaught.java:5) at exception.NeverCaught.g(NeverCaught.java:10) ... 1 more */
initCause() 메소드는 Throwable을 상속하므로 이 예외 체인의 특성은 모든 예외에서 공유됩니다.
예시 4. 정리 작업
IO, JDBC 등 리소스를 많이 소모하는 작업이 있는 경우 정리 작업은 우리에게 필수적입니다. 사용 후 제때에 올바르게 닫지 않으면 결과가 심각해지며 이는 메모리 누수를 의미합니다. 예외가 발생하면 상황에 관계없이 리소스를 적시에 올바르게 정리할 수 있도록 메커니즘을 설계해야 합니다. 드디어입니다.
public void readFile(String file) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream(file))); // do some other work } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
예제는 매우 간단하며 파일을 읽는 예입니다. 이러한 예는 JDBC 작업에서도 매우 일반적입니다. (그래서 리소스를 적시에 올바르게 정리하는 것이 프로그래머의 기본 자질 중 하나라고 생각합니다.)
Try...finally 구조도 리소스가 올바르게 닫히도록 하는 수단이기도 합니다. 코드 실행 중에 리소스 정리를 방해하는 예외가 무엇인지 모르는 경우에는 이 "의심스러운" 코드를 래핑한 다음 최종적으로 리소스를 정리하는 방법을 사용할 수 있습니다. 예를 들어보세요:
public void readFile() { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("file"))); // do some other work //close reader reader.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
이 방법과 이전 방법의 차이점에 주목합시다. 다음 사람은 가능한 한 빨리 독자를 닫는 더 나은 습관을 가질 수 있습니다. 하지만 reader.close() 이전에 언제든지 예외가 발생할 수 있고 이러한 코드 구조로는 예외 발생을 방지할 수 없기 때문에 종종 역효과를 낳습니다. 예외가 발생한 곳에서 프로그램이 튀어나오기 때문에 후속 코드는 실행될 수 없습니다(위의 예를 통해 입증되어야 함). 이때 try...finally를 사용하여 다음과 같이 변환할 수 있습니다.
public void readFile() { BufferedReader reader = null; try { try { reader = new BufferedReader(new InputStreamReader( new FileInputStream("file"))); // do some other work // close reader } finally { reader.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
리소스를 일찍 닫는 것은 좋은 행동입니다. 왜냐하면 시간이 길수록 리소스를 닫을 가능성이 더 커지기 때문입니다. 닫는 것을 잊어버리세요. 이런 방식으로 try...finally의 조정은 완벽하게 보장됩니다(문제가 없다고 생각하지 마십시오. Java는 매우 만족스럽습니다).
다른 상황에 대해 이야기해 보겠습니다. 생성자 메서드에서 파일을 열거나 JDBC 연결을 생성하려는 경우 이 리소스를 다른 메서드에서 사용하려고 하기 때문에 초기에는 수행할 수 없습니다. 생성자 메서드를 닫습니다. 그렇다면 우리는 헤매고 있는 걸까요? 대답은 '아니오'입니다. 다음 예를 살펴보세요.
public class ResourceInConstructor { BufferedReader reader = null; public ResourceInConstructor() { try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(""))); } catch (FileNotFoundException e) { e.printStackTrace(); } } public void readFile() { try { while(reader.readLine()!=null) { //do some work } } catch (IOException e) { e.printStackTrace(); } } public void dispose() { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } }
이 부분은 조금 더 진행되지만 예외는 실제로 사용하기 쉬운 것처럼 보이지만 사용하기 어려운 것입니다. 에 대해 깊이 탐구할 필요가 있다.