1. 소개
아쉽게도 try…catch…finally는 누구에게나 매우 친숙한 표현이고 사용하기 매우 간단하고 논리적으로 이해하기 쉽습니다. 그러나 제가 개인적으로 경험한 "수업"은 이것이 제가 상상했던 것만큼 단순하고 순종적이지 않다는 것을 말해줍니다. 믿을 수 없나요? 그런 다음 아래 코드를 살펴보고 실행 후 결과가 어떻게 될지 "추측"해 볼까요? 답을 다시 보지 말고, 실제 답을 보기 위해 코드를 실행할 수도 없습니다. 답이 정확하다면 이 글을 읽는 데 시간을 낭비할 필요가 없습니다.
package Test; public class TestException { public TestException() { } boolean testEx() throws Exception { boolean ret = true; try { ret = testEx1(); } catch (Exception e) { System.out.println("testEx, catch exception"); ret = false; throw e; } finally { System.out.println("testEx, finally; return value=" + ret); return ret; } } boolean testEx1() throws Exception { boolean ret = true; try { ret = testEx2(); if (!ret) { return false; } System.out.println("testEx1, at the end of try"); return ret; } catch (Exception e) { System.out.println("testEx1, catch exception"); ret = false; throw e; } finally { System.out.println("testEx1, finally; return value=" + ret); return ret; } } boolean testEx2() throws Exception { boolean ret = true; try { int b = 12; int c; for (int i = 2; i >= -2; i--) { c = b / i; System.out.println("i=" + i); } return true; } catch (Exception e) { System.out.println("testEx2, catch exception"); ret = false; throw e; } finally { System.out.println("testEx2, finally; return value=" + ret); return ret; } } public static void main(String[] args) { TestException testException1 = new TestException(); try { testException1.testEx(); } catch (Exception e) { e.printStackTrace(); } } }
당신의 대답은 무엇인가요? 아래에 답이 있나요?
i=2
i=1
testEx2, 예외 잡기
testEx2, 마지막으로 반환 값=false
testEx1, 예외 잡기
testEx1, 마지막으로 반환 값= false
testEx, catch 예외
testEx, finally; return value=false
귀하의 답변이 실제로 위에서 언급한 것과 같다면 틀린 것입니다. ^_^, 그렇다면 이 글을 주의 깊게 읽어보시거나 위의 코드를 사용하여 다양한 상황에서 수정하고 실행하고 테스트해 보시기 바랍니다. 많은 것들이 원래 상상했던 것만큼 간단하지 않다는 것을 알게 될 것입니다. 이제 정답이 발표됩니다.
i=2
i=1
testEx2, catch 예외
testEx2, finally; return value=false
testEx1, finally; false
testEx, finally; return value=false
참고:
finally 문 블록은 나타나지 않아야 하며 return은 나타나야 합니다. 위의 반환 ret은 관련 논리를 처리하기 위해 다른 문에서 가장 잘 사용됩니다.
2. JAVA 예외
비정상이란 파일을 찾을 수 없음, 네트워크 연결 실패, 잘못된 매개변수 등 다양한 예상치 못한 상황을 의미합니다. 예외는 프로그램 실행 중에 발생하여 일반적인 명령 흐름을 방해하는 이벤트입니다. Java는 API에 있는 Throwable 클래스의 수많은 하위 클래스를 통해 다양한 예외를 설명합니다. 따라서 Java 예외는 코딩 부분에서 발생하는 오류 조건을 설명하는 Throwable 하위 클래스의 인스턴스인 객체입니다. 조건이 생성되면 오류가 예외를 발생시킵니다.
Java 예외 클래스 계층 다이어그램:
Java에서 모든 예외는 공통 조상 Throwable(throwable)을 갖습니다. Throwable은 예외 전파 메커니즘을 사용하여 Java 애플리케이션을 통해 전파될 수 있는 코드 문제의 공통성을 지정합니다.
Throwable: 두 가지 중요한 하위 클래스가 있습니다: Exception과 Error. 둘 다 Java 예외 처리의 중요한 하위 클래스이며 각각에는 많은 수의 하위 클래스가 포함되어 있습니다.
오류: 프로그램이 처리할 수 없는 오류로, 애플리케이션 실행에 심각한 문제가 있음을 나타냅니다. 대부분의 오류는 코드 작성자가 수행한 작업과 관련이 없으며 대신 코드가 실행되는 동안 JVM(Java Virtual Machine)의 문제를 나타냅니다. 예를 들어, Java 가상 머신 실행 오류(Virtual MachineError)는 JVM에 작업을 계속 실행하는 데 필요한 메모리 리소스가 더 이상 없으면 OutOfMemoryError가 발생합니다. 이러한 예외가 발생하면 JVM(Java Virtual Machine)은 일반적으로 스레드를 종료하도록 선택합니다.
이러한 오류는 Java 가상 머신 실행 오류(Virtual MachineError), 클래스 정의 오류(NoClassDefFoundError) 등 가상 머신 자체에서 결함이 발생하거나 가상 머신이 애플리케이션을 실행하려고 할 때 발생함을 나타냅니다. 이러한 오류는 응용 프로그램의 제어 및 처리 능력 범위를 벗어나기 때문에 확인할 수 없으며, 대부분은 프로그램 실행 중에 발생하도록 허용되지 않는 조건입니다. 잘 설계된 애플리케이션의 경우 오류가 발생하더라도 이로 인해 발생한 예외 조건을 처리하려는 시도가 있어서는 안 됩니다. Java에서 오류는 Error의 하위 클래스를 통해 설명됩니다.
예외: 프로그램 자체에서 처리할 수 있는 예외입니다.
Exception 클래스에는 중요한 하위 클래스인 RuntimeException이 있습니다. RuntimeException 클래스와 해당 하위 클래스는 "일반 JVM 작업"으로 인해 발생한 오류를 나타냅니다. 예를 들어 null 개체 참조를 사용하려고 하거나 0으로 나누거나 배열이 범위를 벗어나는 경우 런타임 예외(NullPointerException, ArithmeticException) 및 ArrayIndexOutOfBoundException이 각각 발생합니다.
참고: 예외와 오류의 차이점: 예외는 프로그램 자체에서 처리할 수 있지만 오류는 처리할 수 없습니다.
일반적으로 Java 예외(Exception 및 Error 포함)는 확인 가능한 예외(checked 예외)와 확인되지 않은 예외(unchecked 예외)로 구분됩니다.
검사 가능한 예외(컴파일러에서 처리해야 하는 예외): 올바른 프로그램이 실행될 때 발생하기 쉽고 허용할 수 있는 예외입니다. 확인 가능한 예외는 비정상적인 상황이지만 어느 정도 발생을 예상할 수 있으며, 이러한 비정상적인 상황이 발생하면 어떤 방식으로든 처리해야 합니다.
RuntimeException 및 해당 하위 클래스를 제외하고 다른 Exception 클래스 및 해당 하위 클래스는 확인 가능한 예외입니다. 이러한 종류의 예외의 특징은 Java 컴파일러가 이를 확인한다는 것입니다. 즉, 이러한 종류의 예외가 프로그램에서 발생할 수 있으면 try-catch 문을 사용하여 이를 포착하거나 throws 절을 사용하여 선언합니다. 그렇지 않으면 컴파일이 통과되지 않습니다.
검사할 수 없는 예외(컴파일러에서 강제 처리가 필요하지 않은 예외): 런타임 예외(RuntimeException 및 해당 하위 클래스) 및 오류(Error)가 포함됩니다.
예외 이 예외는 런타임 예외와 비런타임 예외(컴파일 예외)라는 두 가지 주요 범주로 나뉩니다. 프로그램은 이러한 예외를 가능한 한 많이 처리해야 합니다.
런타임 예외: NullPointerException(널 포인터 예외), IndexOutOfBoundsException(아래 첨자 범위를 벗어난 예외) 등과 같은 RuntimeException 클래스 및 해당 하위 클래스의 모든 예외입니다. 이러한 예외는 확인된 예외가 아닙니다. 프로그램에서 캡처하거나 처리하지 않도록 선택할 수 있습니다. 이러한 예외는 일반적으로 프로그램 논리 오류로 인해 발생하므로 프로그램은 논리적 관점에서 이러한 예외가 발생하지 않도록 노력해야 합니다.
런타임 예외의 특징은 Java 컴파일러가 이를 확인하지 않는다는 것입니다. 즉, 이러한 유형의 예외가 프로그램에서 발생할 수 있으면 try-catch 문으로 캡처하지 않더라도 throws 절로 선언되지 않으면 컴파일됩니다.
비런타임 예외(컴파일 예외): RuntimeException 이외의 예외이며 유형이 Exception 클래스 및 해당 하위 클래스에 속합니다. 프로그램 구문의 관점에서 처리해야 하는 예외입니다. 처리되지 않으면 프로그램이 컴파일되지 않습니다. IOException, SQLException 등 및 사용자 정의 예외는 일반적으로 사용자 정의 확인 예외가 사용되지 않습니다.
3. 예외 처리 메커니즘
Java 애플리케이션에서 예외 처리 메커니즘은 예외 발생 및 예외 포착입니다.
예외 발생: 메소드에서 오류가 발생하여 예외가 발생하면 메소드는 예외 객체를 생성하여 런타임 시스템에 전달합니다. 예외 객체에는 예외 유형 및 예외 정보가 포함됩니다. 예외가 발생할 때 프로그램 상태. 런타임 시스템은 예외를 처리할 코드를 찾아 실행하는 역할을 담당합니다.
예외 잡기: 메서드에서 예외가 발생한 후 런타임 시스템은 적절한 예외 처리기를 찾습니다. 잠재적인 예외 처리기는 예외가 발생할 때 호출 스택에 순서대로 남아 있는 메서드 모음입니다. 예외 처리기가 처리할 수 있는 예외 유형이 메서드에서 발생한 예외 유형과 일치하면 적합한 예외 처리기입니다. 런타임 시스템은 예외가 발생한 메서드부터 시작하여 적절한 예외 처리기가 포함된 메서드를 찾아서 실행할 때까지 호출 스택의 메서드를 다시 확인합니다. 런타임 시스템이 호출 스택을 탐색하고 적절한 예외 핸들러를 찾지 못하면 런타임 시스템이 종료됩니다. 동시에 이는 Java 프로그램의 종료를 의미합니다.
런타임 예외, 오류 또는 확인 가능한 예외의 경우 Java 기술에서 요구하는 예외 처리 방법이 다릅니다.
런타임 예외는 검사할 수 없기 때문에 애플리케이션을 보다 합리적이고 쉽게 구현하기 위해 Java에서는 런타임 예외가 Java 런타임 시스템에 의해 자동으로 발생되도록 규정하여 애플리케이션이 런타임 예외를 무시할 수 있도록 합니다.
메소드 실행 중 발생할 수 있는 오류의 경우, 실행 중인 메소드가 이를 포착하기를 원하지 않을 때 Java에서는 해당 메소드가 Throw문을 작성하지 않도록 허용합니다. 대부분의 오류 예외는 발생해서는 안 되는 상황이자 합리적인 애플리케이션이 포착해서는 안 되는 예외이기 때문입니다.
확인 가능한 모든 예외에 대해 Java는 메서드가 이를 포착하거나 메서드 외부에서 발생하도록 선언해야 한다고 규정합니다. 즉, 메소드가 확인 가능한 예외를 포착하지 않기로 선택한 경우 예외를 발생시키겠다고 선언해야 합니다.
예외를 포착할 수 있는 메서드는 해당 유형의 예외 처리기를 제공해야 합니다. 캡처된 예외는 자체 명령문에 의해 발생 및 발생한 예외일 수도 있고, 호출된 메소드 또는 Java 런타임 시스템에 의해 발생한 예외일 수도 있습니다. 즉, 메소드가 포착할 수 있는 예외는 Java 코드 어딘가에서 발생한 예외여야 합니다. 간단히 말해서 예외는 항상 먼저 발생하고 나중에 포착됩니다.
직접 작성한 코드, Java 개발 환경 패키지의 코드 또는 Java 런타임 시스템과 같은 모든 Java 코드는 예외를 발생시킬 수 있습니다. 누구나 Java의 throw 문을 통해 예외를 던질 수 있습니다.
메서드에서 발생하는 모든 예외는 throws 절을 사용해야 합니다.
예외 포착은 try-catch 문 또는 try-catch-finally 문을 통해 수행됩니다.
일반적으로 Java에서는 확인 가능한 예외를 포착하거나 throw하도록 선언해야 한다고 규정합니다. 확인할 수 없는 RuntimeException 및 오류를 무시할 수 있습니다.
3.1 捕获异常:try、catch 和 finally
1.try-catch语句
在Java中,异常通过try-catch语句捕获。其一般语法形式为:
try { // 可能会发生异常的程序代码 } catch (Type1 id1){ // 捕获并处置try抛出的异常类型Type1 } catch (Type2 id2){ //捕获并处置try抛出的异常类型Type2 }
关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。Java方法在运行过程中出现异常,则创建异常对象。将异常抛出监控区域之 外,由Java运行时系统试图寻找匹配的catch子句以捕获异常。若有匹配的catch子句,则运行其异常处理代码,try-catch语句结束。
匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。
例1 捕捉throw语句抛出的“除数为0”异常。
public class TestException { public static void main(String[] args) { int a = 6; int b = 0; try { // try监控区域 if (b == 0) throw new ArithmeticException(); // 通过throw语句抛出异常 System.out.println("a/b的值是:" + a / b); } catch (ArithmeticException e) { // catch捕捉异常 System.out.println("程序出现异常,变量b不能为0。"); } System.out.println("程序正常结束。"); } }
运行结果:
程序出现异常,变量b不能为0。
程序正常结束。
例1 在try监控区域通过if语句进行判断,当“除数为0”的错误条件成立时引发ArithmeticException异常,创建 ArithmeticException异常对象,并由throw语句将异常抛给Java运行时系统,由系统寻找匹配的异常处理器catch并运行相应异 常处理代码,打印输出“程序出现异常,变量b不能为0。”try-catch语句结束,继续程序流程。
事实上,“除数为0”等ArithmeticException,是RuntimException的子类。而运行时异常将由运行时系统自动抛出,不需要使用throw语句。
例2 捕捉运行时系统自动抛出“除数为0”引发的ArithmeticException异常。
public static void main(String[] args) { int a = 6; int b = 0; try { System.out.println("a/b的值是:" + a / b); } catch (ArithmeticException e) { System.out.println("程序出现异常,变量b不能为0。"); } System.out.println("程序正常结束。"); } }
运行结果:
程序出现异常,变量b不能为0。
程序正常结束。
例2 中的语句:
System.out.println(“a/b的值是:” + a/b);
在运行中出现“除数为0”错误,引发ArithmeticException异常。运行时系统创建异常对象并抛出监控区域,转而匹配合适的异常处理器catch,并执行相应的异常处理代码。
由于检查运行时异常的代价远大于捕捉异常所带来的益处,运行时异常不可查。Java编译器允许忽略运行时异常,一个方法可以既不捕捉,也不声明抛出运行时异常。
例3 不捕捉、也不声明抛出运行时异常。
public class TestException { public static void main(String[] args) { int a, b; a = 6; b = 0; // 除数b 的值为0 System.out.println(a / b); } }
运行结果:
Exception in thread “main” java.lang.ArithmeticException: / by zero
at Test.TestException.main(TestException.java:8)
例4 程序可能存在除数为0异常和数组下标越界异常。
public class TestException { public static void main(String[] args) { int[] intArray = new int[3]; try { for (int i = 0; i <= intArray.length; i++) { intArray[i] = i; System.out.println("intArray[" + i + "] = " + intArray[i]); System.out.println("intArray[" + i + "]模 " + (i - 2) + "的值: " + intArray[i] % (i - 2)); } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("intArray数组下标越界异常。"); } catch (ArithmeticException e) { System.out.println("除数为0异常。"); } System.out.println("程序正常结束。"); } }
运行结果:
intArray[0] = 0
intArray[0]模 -2的值: 0
intArray[1] = 1
intArray[1]模 -1的值: 0
intArray[2] = 2
除数为0异常。
程序正常结束。
例4 程序可能会出现除数为0异常,还可能会出现数组下标越界异常。程序运行过程中ArithmeticException异常类型是先行匹配的,因此执行相匹配的catch语句:
catch (ArithmeticException e){ System.out.println("除数为0异常。"); }
需要注意的是,一旦某个catch捕获到匹配的异常类型,将进入异常处理代码。一经处理结束,就意味着整个try-catch语句结束。其他的catch子句不再有匹配和捕获异常类型的机会。
Java通过异常类描述异常类型,异常类的层次结构如图1所示。对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子 句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。
RuntimeException异常类包括运行时各种常见的异常,ArithmeticException类和ArrayIndexOutOfBoundsException类都是它的子类。因此,RuntimeException异常类的catch子句应该放在 最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。
2. try-catch-finally语句
try-catch语句还可以包括第三部分,就是finally子句。它表示无论是否出现异常,都应当执行的内容。try-catch-finally语句的一般语法形式为:
try { // 可能会发生异常的程序代码 } catch (Type1 id1) { // 捕获并处理try抛出的异常类型Type1 } catch (Type2 id2) { // 捕获并处理try抛出的异常类型Type2 } finally { // 无论是否发生异常,都将执行的语句块 }
例5 带finally子句的异常处理程序。
public class TestException { public static void main(String args[]) { int i = 0; String greetings[] = { " Hello world !", " Hello World !! ", " HELLO WORLD !!!" }; while (i < 4) { try { // 特别注意循环控制变量i的设计,避免造成无限循环 System.out.println(greetings[i++]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("数组下标越界异常"); } finally { System.out.println("--------------------------"); } } } }
运行结果:
Hello world !
————————–
Hello World !!
————————–
HELLO WORLD !!!
————————–
数组下标越界异常
————————–
在例5中,请特别注意try子句中语句块的设计,如果设计为如下,将会出现死循环。如果设计为:
try { System.out.println (greetings[i]); i++; }
小结:
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
3. try-catch-finally 规则(异常处理语句的语法规则):
1) 必须在 try 之后添加 catch 或 finally 块。try 块后可同时接 catch 和 finally 块,但至少有一个块。
2) 必须遵循块顺序:若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
3) catch 块与相应的异常类的类型相关。
4) 一个 try 块可能有多个 catch 块。若如此,则执行第一个匹配块。即Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个catch代码块,不会再执行其他的 catch代码块
5) 可嵌套 try-catch-finally 结构。
6) 在 try-catch-finally 结构中,可重新抛出异常。
7) 除了下列情况,总将执行 finally 做为结束:JVM 过早终止(调用 System.exit(int));在 finally 块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击。
4. try、catch、finally语句块的执行顺序:
1)当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
图示try、catch、finally语句块的执行:
3.2 抛出异常
任何Java代码都可以抛出异常,如:自己编写的代码、来自Java开发环境包中代码,或者Java运行时系统。无论是谁,都可以通过Java的throw语句抛出异常。从方法中抛出的任何异常都必须使用throws子句。
1. throws抛出异常
如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。例如汽车在运行时可能会出现故障,汽车本身没办法处理这个故障,那就让开车的人来处理。
throws语句用在方法定义时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分割。throws语句的语法格式为:
methodname throws Exception1,Exception2,..,ExceptionN { }
方法名后的throws Exception1,Exception2,…,ExceptionN 为声明要抛出的异常列表。当方法抛出异常列表的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。例如:
import java.lang.Exception; public class TestException { static void pop() throws NegativeArraySizeException { // 定义方法并抛出NegativeArraySizeException异常 int[] arr = new int[-3]; // 创建数组 } public static void main(String[] args) { // 主方法 try { // try语句处理异常信息 pop(); // 调用pop()方法 } catch (NegativeArraySizeException e) { System.out.println("pop()方法抛出的异常");// 输出异常信息 } } }
使用throws关键字将异常抛给调用者后,如果调用者不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的调用者。
pop方法没有处理异常NegativeArraySizeException,而是由main函数来处理。
Throws抛出异常的规则:
1) 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
2)必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
3)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
例如:
void method1() throws IOException{} //合法 //编译错误,必须捕获或声明抛出IOException void method2(){ method1(); } //合法,声明抛出IOException void method3()throws IOException { method1(); } //合法,声明抛出Exception,IOException是Exception的子类 void method4()throws Exception { method1(); } //合法,捕获IOException void method5(){ try{ method1(); }catch(IOException e){…} } //编译错误,必须捕获或声明抛出Exception void method6(){ try{ method1(); }catch(IOException e){throw new Exception();} } //合法,声明抛出Exception void method7()throws Exception{ try{ method1(); }catch(IOException e){throw new Exception();} }
判断一个方法可能会出现异常的依据如下:
1)方法中有throw语句。例如,以上method7()方法的catch代码块有throw语句。
2)调用了其他方法,其他方法用throws子句声明抛出某种异常。例如,method3()方法调用了method1()方法,method1()方法声明抛出IOException,因此,在method3()方法中可能会出现IOException。
2. 使用throw抛出异常
throw总是出现在函数体中,用来抛出一个Throwable类型的异常。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
我们知道,异常是异常类的实例对象,我们可以创建异常类的实例对象通过throw语句抛出。该语句的语法格式为:
throw new exceptionname;
例如抛出一个IOException类的异常对象:
throw new IOException;
要注意的是,throw 抛出的只能够是可抛出类Throwable 或者其子类的实例对象。下面的操作是错误的:
throw new String(“exception”);
这是因为String 不是Throwable 类的子类。
如果抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。
如果所有方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。如果抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。
package Test; import java.lang.Exception; public class TestException { static int quotient(int x, int y) throws MyException { // 定义方法抛出异常 if (y < 0) { // 判断参数是否小于0 throw new MyException("除数不能是负数"); // 异常信息 } return x/y; // 返回值 } public static void main(String args[]) { // 主方法 int a =3; int b =0; try { // try语句包含可能发生异常的语句 int result = quotient(a, b); // 调用方法quotient() } catch (MyException e) { // 处理自定义异常 System.out.println(e.getMessage()); // 输出异常信息 } catch (ArithmeticException e) { // 处理ArithmeticException异常 System.out.println("除数不能为0"); // 输出提示信息 } catch (Exception e) { // 处理其他异常 System.out.println("程序发生了其他的异常"); // 输出提示信息 } } } class MyException extends Exception { // 创建自定义异常类 String message; // 定义String类型变量 public MyException(String ErrorMessagr) { // 父类方法 message = ErrorMessagr; } public String getMessage() { // 覆盖getMessage()方法 return message; } }
3.3 异常链
1) 如果调用quotient(3,-1),将发生MyException异常,程序调转到catch (MyException e)代码块中执行;
2) 如果调用quotient(5,0),将会因“除数为0”错误引发ArithmeticException异常,属于运行时异常类,由Java运行时系统自动抛出。quotient()方法没有捕捉ArithmeticException异常,Java运行时系统将沿方法调用栈查到main方法,将抛出的异常上传至quotient()方法的调用者:
int result = quotient(a, b); // 调用方法quotient()
由于该语句在try监控区域内,因此传回的“除数为0”的ArithmeticException异常由Java运行时系统抛出,并匹配catch子句:
catch (ArithmeticException e) { // 处理ArithmeticException异常 System.out.println(“除数不能为0″); // 输出提示信息 }
处理结果是输出“除数不能为0”。Java这种向上传递异常信息的处理机制,形成异常链。
Java方法抛出的可查异常将依据调用栈、沿着方法调用的层次结构一直传递到具备处理能力的调用方法,最高层次到main方法为止。如果异常传递到main方法,而main不具备处理能力,也没有通过throws声明抛出该异常,将可能出现编译错误。
3)如还有其他异常发生,将使用catch (Exception e)捕捉异常。由于Exception是所有异常类的父类,如果将catch (Exception e)代码块放在其他两个代码块的前面,后面的代码块将永远得不到执行,就没有什么意义了,所以catch语句的顺序不可掉换。
3.4 Throwable类中的常用方法
注意:catch关键字后面括号中的Exception类型的参数e。Exception就是try代码块传递给catch代码块的变量类型,e就是变量名。catch代码块中语句”e.getMessage();”用于输出错误性质。通常异常处理常用3个函数来获取异常的有关信息:
getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。
getMeage():返回异常的消息信息。
printStackTrace(): 객체의 스택 추적은 System.err 필드의 값으로 오류 출력 스트림에 출력됩니다.
단순화를 위해 catch 문 뒤의 코드를 무시하여 try-catch 문이 장식이 되는 경우도 있습니다. 프로그램 실행 중에 예외가 발생하면 예외 처리가 무시되고 오류가 발생합니다. 발생 원인을 찾기가 어렵습니다.
4. Java 공통 예외
Java에서는 자주 발생하는 오류를 설명하기 위해 몇 가지 예외를 제공합니다. 이러한 예외의 경우 일부는 프로그래머가 해당 예외를 캡처하거나 선언해야 하며 일부는 캡처 처리가 자동으로 필요합니다. Java 가상 머신에 의해 수행됩니다. Java의 일반적인 예외 클래스:
1. RuntimeException 하위 클래스:
1. java.lang.ArrayIndexOutOfBoundsException
배열 인덱스 범위를 벗어난 예외입니다. 배열의 인덱스가 음수이거나 배열 크기보다 크거나 같을 때 발생합니다.
2. java.lang.ArithmeticException
산술 조건 예외입니다. 예: 0으로 정수 나누기 등
3. java.lang.NullPointerException
널 포인터 예외. 이 예외는 애플리케이션이 객체가 필요한 곳에 null을 사용하려고 시도할 때 발생합니다. 예를 들어 null 객체의 인스턴스 메서드 호출, null 객체의 속성에 액세스, null 객체의 길이 계산, throw 문을 사용하여 null을 던지는 등
4. java.lang. ClassNotFoundException
클래스 예외를 찾을 수 없습니다. 이 예외는 애플리케이션이 문자열 형식의 클래스 이름을 기반으로 클래스를 구성하려고 시도하지만 CLASSPAH를 순회한 후 해당 이름을 가진 클래스 파일을 찾을 수 없는 경우 발생합니다.
5. java.lang.NegativeArraySizeException 배열 길이가 음수 예외입니다.
6. java.lang.ArrayStoreException 배열에 호환되지 않는 값 예외가 포함되어 있습니다.
7. java.lang .SecurityException 보안 예외
8. java.lang.IllegalArgumentException 잘못된 매개변수 예외
2.IOException
IOException: 입력 스트림 및 출력 스트림 예외를 실행할 때 발생할 수 있습니다.
EOFException 파일 종료 예외
FileNotFoundException 파일을 찾을 수 없는 예외
3. 기타
ClassCastException 유형 변환 예외 클래스
ArrayStoreException Array Exception 호환되지 않는 값이 포함되어 발생함
SQLException 작업 데이터베이스 예외 클래스
NoSuchFieldException 필드를 찾을 수 없음 예외
NoSuchMethodException 메소드를 찾을 수 없음 예외 발생
NumberFormatException 예외 발생 문자열을 숫자로 변환할 때
StringIndexOutOfBoundsException 문자열 인덱스가 범위를 벗어날 때 발생하는 예외
IllegalAccessException 특정 유형의 예외에 대한 접근이 허용되지 않습니다
InstantiationException 적용되는 경우 프로그램이 Class 클래스의 newInstance() 메서드를 사용하여 클래스의 인스턴스를 생성하려고 시도하고 지정된 클래스 객체를 인스턴스화할 수 없는 경우 이 예외가 발생합니다
5. 사용자 정의 예외
Java에 내장된 예외 클래스를 사용하면 프로그래밍 중에 발생하는 대부분의 예외를 설명할 수 있습니다. 또한 사용자는 예외를 사용자 정의할 수도 있습니다. 사용자 정의 예외 클래스는 Exception 클래스만 상속하면 됩니다.
프로그램에서 사용자 정의 예외 클래스를 사용하는 방법은 대략 다음 단계로 나눌 수 있습니다.
(1) 사용자 정의 예외 클래스를 만듭니다.
(2) 메소드의 throw 키워드를 통해 예외 객체를 던집니다.
(3) 현재 예외가 발생한 메소드에서 예외가 처리되는 경우 try-catch 문을 사용하여 이를 캡처하고 처리할 수 있으며, 그렇지 않으면 메소드 선언에서 throws 키워드를 사용하여 예외를 나타냅니다. 메서드 호출자에게 전달되고 계속 다음 단계로 이동합니다.
(4) 예외 메소드 호출자에서 예외를 포착하고 처리합니다.
위의 예에서는 "throw를 사용하여 예외를 발생시키는 방법"이 언급되었습니다.