異常
異常的概述
- 異常就是不正常的意思,Java語言中主要是指程序在運行階段產生的錯誤
- Throwable(可拋出的,可扔出的)
- java.lang.Throwable 類是Java程序所有錯誤或異常的超類
- 主要有兩個字類
- Error
- Error主要描述比較嚴重的錯誤
- 無法通過編程來解決的重大的錯誤
- Exception
- Exception主要m描述比較輕量級的錯誤
- 可以通過編程來解決
- Error
Exception類的主要分類
RuntimeException -> 運行時異常,也叫非檢測性異常類
- 非檢測性異常類就是指b編譯階段無法被編譯器檢測出來的異常
- 主要子類
- ArithmeticException -> 算數異常類
- ArrayIndexOutOfBoundsException(間接子類) -> 數組下標異常類
- NullPointerException -> 空指針異常
- ClassCastException -> 類型轉換異常
- NumberFormatException(間接子類)-> 數字格式異常
- 注意
- 當程序的執行過程中產生異常,若沒有手動進行處理,則由Java虛擬機採用默認的方式進行處理,默認方式是打印異常名稱、異常原因以及異常發生的位置並終止程序,後序代碼無法被執行
IOException和其他異常類 -> 其他異常類,也叫做非檢測性異常
案例
TestRuntimeException.java
package demo1;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
* ArithmeticException -> 算數異常類
ArrayIndexOutOfBoundsException(間接子類) -> 數組下標異常類
NullPointerException -> 空指針異常
ClassCastException -> 類型轉換異常
NumberFormatException(間接子類)-> 數字格式異常
*/
public class TestRuntimeException {
public static void main(String[] args) {
// 觀察檢測性異常
// FileInputStream fis = new FileInputStream("c:/a.txt");
// java.lang.ArithmeticException 算數異常
int a = 10;
int b = 0;
if (b != 0) {
System.out.println(a/b);
}
// java.lang.ArrayIndexOutOfBoundsException 數組下標越界異常
int[] arr = new int[3];
int num = 3;
if (num >= 0 && num < arr.length) {
System.out.println(arr[num]);
}
// java.lang.NullPointerException
String str = null;
if (str != null) {
System.out.println(str.length());
}
// java.lang.ClassCastException
Exception ex = new Exception();
if (ex instanceof IOException) {
IOException ie = (IOException) ex;
}
// java.lang.NumberFormatException
String s = "12be";
if (s.matches("\\d+")) {
System.out.println(Integer.parseInt(s));
}
System.out.println("運行程序結束");
}
}
異常處理
運行時異常的處理方式
- 對於絕大數運行時異常來說,都可以通過條件判斷避免異常的發生
異常的捕獲
-
語法格式
try{
可能產生異常對象的語句塊
}catch(異常類型 引用名){
針對當前異常類型對象的處理語句塊
}
… (可以寫多個catch)
finally{
無論是否發生異常,都應該執行的語句塊
} -
注意事項
- 當捕獲的結構中有多個catch分支時,切記小範圍的異常類型放在大範圍的異常類型上面
- 懶人寫法:
catch(Exception e) {…}
-
執行流程
try { a; b; // 可能產生異常的語句 c; } catch (Exception e) { e; } finally { f; }
- 當沒有產生異常的時候,程序的執行流程是:a b c f
- 當產生異常時,程序的執行流程是: a b e f
-
案例
- TestExceptionCatch.java
package demo2; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class TestExceptionCatch { public static void main(String[] args) { // 聲明引用指向本類的對象,用於讀取文件中的內容 FileInputStream fis = null; try { // 隨時可能產生文件找不到y異常對象, new FileNotFoundException() fis = new FileInputStream("d:/a.txt"); } catch (FileNotFoundException e) { // 打印異常的名稱、異常原因、異常的位置等信息 e.printStackTrace(); } try { fis.close(); } catch (IOException e) { e.printStackTrace(); } catch (NullPointerException e) { // System.out.println("輸出"); e.printStackTrace(); } } }
- TestFinally.java
package demo3; public class TestFinally { public static void main(String[] args) { int a = 10; int b = 0; try { System.out.println(a/b); } catch (Exception e) { e.printStackTrace(); return ; } finally { System.out.println("無論是否發生異常都會執行"); } System.out.println("程序結束"); } }
java.lang.ArithmeticException: / by zero 無論是否發生異常都會執行 at demo3.TestFinally.main(TestFinally.java:9)
異常的拋出
- 基本概念
- 某些特殊的場合中,當產生異常後卻無法直接處理/不想處理時,此時就可以將異常轉移給當前方法的調用者,這就叫異常的拋出
- 語法格式
- 返回值類型 方法名稱(形參列表) throws 異常類型{…}
- 方法重寫的原則
- 要求方法名相同、參數列表相同、返回值類型也相同,從jdk1.5開始允許返回子類類型
- 範圍權限不能變小,可以相同或者變大
- 不能拋出更大的異常
- 注意
- 子類中重寫以後的方法可以選擇拋出與父類一樣的異常、更小的異常、不拋出異常,但是不能拋出更大的異常、不同的異常
- 案例
A.java
SubA.javapackage demo4; import java.io.IOException; public class A { public void show() throws IOException{ System.out.println("A"); } }
package demo4; import java.io.IOException; import java.io.FileNotFoundException; import javax.print.PrintException; public class SubA extends A{ @Override // public void show() throws IOException { // public void show() throws FileNotFoundException { // public void show() throws PrintException { // public void show() throws Exception { public void show() { } }
顯然不能拋出更大的異常
自定義異常
-
自定義異常的由來
- Java官方庫中雖然提供了大量的異常類,但不足以描述現實生活中所有的異常情況。當出現官方庫中沒有m描述的異常情況,這個時候就需要程序員自定義異常類加以描述,使得異常信息更加具備針對性和可讀性
-
自定義異常的流程
- 自定義類繼承自Exception類或者Exception類的子類
- 提供兩個版本的構造方法,一個是無參構造方法,另一個是字符串做參數的構造方法
-
自定義異常 – >案例1
- Person.java
package demo5; public class Person { private String name; private int age; public Person() { super(); } public Person(String name, int age) throws Exception { super(); setName(name); setAge(age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) throws Exception { if(age > 0 && age < 150) { this.age = age; } else { // System.out.println("年齡不合理"); // 手動產生一個異常對象並拋出 // throw new Exception(); throw new AgeException("年齡不合理!"); } System.out.println("產生異常的效果"); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
- AgeException.java
package demo5; public class AgeException extends Exception { /** * */ private static final long serialVersionUID = 1L; // 自定義無參的構造方法 public AgeException() { } // 自定義使用字符串作爲參數的構造方法 public AgeException(String msg) { super(msg); } }
- TestPerson.java
package demo5; public class TestPerson { public static void main(String[] args) { Person p = null; try { p = new Person("張三", -12); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(p); } }
demo5.AgeException: 年齡不合理! null at demo5.Person.setAge(Person.java:33) at demo5.Person.<init>(Person.java:15) at demo5.TestPerson.main(TestPerson.java:8)
-
自定義異常 – > 案例2
- Student.java
package demo6; public class Student { private String name; private int id; public Student() { super(); } public Student(String name, int id) throws IDException { super(); setName(name); setId(id); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) throws IDException { if (id > 0) { this.id = id; } else { // System.out.println("學號不合理"); throw new IDException("學號不合理"); } System.out.println("結束"); } @Override public String toString() { return "Student [name=" + name + ", id=" + id + "]"; } }
- IDException.java
package demo6; public class IDException extends Exception { /** * */ private static final long serialVersionUID = 1L; public IDException() { } public IDException(String msg) { super(msg); } }
- TestStudent.java
package demo6; public class TestStudent { public static void main(String[] args) throws IDException { /*Student stu = null; try { stu = new Student("小王", -5); } catch (IDException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ Student stu = new Student("小王", -5); System.out.println(stu); } }
Exception in thread "main" demo6.IDException: 學號不合理 at demo6.Student.setId(Student.java:30) at demo6.Student.<init>(Student.java:14) at demo6.TestStudent.main(TestStudent.java:14)
此處有一點要注意,在案例一的TestPerson中,在main函數中,我們使用try…catch語句,異常由內部進行處理,會打印後面的語句
而在案例二的TestStudent中,我們將異常直接交給main函數處理,也就是交給虛擬機處理,所以並不會執行後面的語句
異常對象的拋出
- throw new 異常類型()
- 例如:
- throw new Exception()
最後簡單介紹一下throws和throw的區別
throws和throw的區別
- throws
- 用在方法聲明後面,跟的是異常類名
- 可以跟多個異常類名,用逗號隔開
- 表示拋出異常,由該方法的調用者來處理
- throws表示出現異常的一種可能性,並不一定會發生這些異常
- throw
- 用在方法體內,跟的異常對象名
- 只能拋出一個異常對象名
- 表示拋出異常,由方法體內的語句實現
- throw則是拋出了異常,執行throw則一定拋出了某種異常