在沒有學習異常之前,我們所有的程序都是默認在理想狀態下運行的,在實際開發中可能存在各種各樣的問題,比如用戶輸入的數值不正確,,格式不正確,都會引起程序運行的錯誤。所謂的異常就是程序運行時出現的不正常的情況。異常也是現實生活中一個個具體的事物,java中的異常時通過java類的形式進行描述,並且封裝成對象的。異常對象都是派生於Throwable類的一個實例。
二,java中的異常的體系結構:
Throwable
|——Error
|——Exception
|——RuntimeException
|——IOException
Error是嚴重異常,是JVM處理不了的異常,所以用戶也處理不了,所以我們不需要去管。當然這種嚴重異常出錯的概率也是比較小的。我們重點討論的是Exception,需要我們程序員去處理的東西。
1,RuntimeException:
Exception的一個特殊子類異常RuntimeException(運行時異常),該異常在函數
部通過throw關鍵字拋出後可以不用再函數上聲明,編譯一樣會通過,如果在函數上聲明,調用者不用進行處理,也會通過;(面試常考)
2,體系中的省略號是指繼承Exception,就是Exception的除RuntimeException的子類。繼承Exception的子類,在函數內部拋出異常對象如果不在函數上聲明,則編譯不能通過,聲明之後調用者不進行處理,編譯也不能通過;
之所以不在函數上聲明是因爲不希望調用者處理,當該異常發生時,希望程序停止,因爲在運行時出現了無法繼續運行的情況,這時就需要停止程序對代碼進行修改;
3,異常體系的特點:異常體系中所有類以及建立的對象都具有可拋性。
也就是說可以被throw和throws關鍵字操作;只有異常體系具備這個特點;
throw和throws的用法:
throw定義在函數內部,用於拋出異常對象;
throws定義在函數上,用於拋出異常類,可以拋出多個用逗號隔開;
當內容有throw拋出的異常對象,並未進行try處理必須要在函數上用throws關鍵字拋出(聲明),否則編譯失敗;
注意:函數內如果拋出的是RuntimeException,函數上可以不用聲明。
三,異常的處理:
1,如果在函數上面進行聲明過的異常,也就是用關鍵字throws在函數上面聲明的異常,當在某個方法中調用這樣的方法的時候,要麼執行try...catch處理,要麼向上一級拋出去,也就是在這個函數上面繼續throws。這一點我們在使用java文檔的時候可能會看到很多。當然不是所有的異常都可以進行throws的,將會在後面的筆記中總結到。
2,異常的分類:有兩種:
(1),編譯時被檢測異常;
該異常在編譯時如果沒有處理(沒有拋,也沒有try)則編譯失敗。
該異常被標示,代表可以被處理;
(2),運行時異常,編譯時不檢測;
編譯時不需要處理,編譯不檢查;
該異常發生時,建議不要處理,讓程序停止,需要對代碼進行修正;
3,異常處理的語句:
try{
需要被檢測的代碼;
}catch(){
處理異常的代碼;
}finally{
一定會執行的代碼;
}
三種形式:
1,形式一:
try{
}catch(){
}finally{
}
2,形式二
try{
}catch(){
}
3,形式三
try{
}finally{
}
Finally特點:
(1),fianlly裏面通常定義的是關閉資源的代碼,因爲資源必須釋放;
(2),finally只有一種情況下不會執行,當執行到System.exit(0)時,JVM結束,finally不會執行;
四,自定義異常方法:
1,自定義異常:是按照java的面型對象思想,將程序的特有問題進行封裝;
在自定義異常中可以有兩種選擇,要麼繼承Exception要麼繼承RuntimeException
本程序重點講解這兩種用戶自定義異常父類的不同。
(1),爲了讓該自定義類具有可拋性;
(2),讓該類具備操作異類的共性方法;
當要定義自定義異常信息的時候,可以使用父類已經定義好的功能。異常信息傳遞給父類的構造函數;(多查看java文檔)
class MyException extends Exception
{
MyException(String message) {
super(message);
}
}
示例一:編寫一個程序,計算一個數除以另外一個數,在相除的時候,除數可能爲零。定義一個負數異常繼承Exception,當除數爲0的時候,拋出異常,打印在控制檯,並且終止整個程序,下面的程序不在執行。
class FushuException extends Exception {
/*
* private String msg; FushuException(String msg) { this.msg = msg; }
*
* public String getMessage() { return msg; }
*/
private int value;
FushuException(String msg, int value) {
super(msg);
this.value = value;
}
public int getValue() {
return value;
}
}
class Demon5 {
public int account(int a, int b) throws FushuException {
if (b < 0)
throw new FushuException("出現了負數的情況;", b);// 手動通過關鍵字自定義一個負數異常;
int c = a / b;
return c;
}
}
class ExceptionTest {
public static void main(String[] args) {
Demon5 d = new Demon5();
try {
System.out.println(d.account(4, -5));
} catch (FushuException e) {
System.out.println(e.toString());
// System.out.println("除數不能爲負數!");
System.out.println("錯誤的負數是" + e.getValue());
}
}
}
示例二:將示例一的程序進行簡化,可以直接將負數異常通過throw關鍵字在函數內部拋出,這時候就不需要在函數上面通過throws拋給上一級。下面的程序負數異常時繼承自RuntimeException,當然也可以不用定義,直接throw new RuntimeException(“異常提示”);
class Test3 {
int div(int a, int b) {
if (b < 0)
throw new FushuException2("除數爲負數了;");// 該異常發生後計算無法繼續,希望程序停止,不需要在
// 函數上聲明;
// 可以不用定義FushuException2,直接throw new RuntimeException("除數爲零");
return a / b;
}
}
class ExceptionDemon {
public static void main(String[] args) {
Test6 t6 = new Test6();
int c = t6.div(5, -8);
System.out.println(c);
}
}
示例三:自定義繼承自Exception和RuntimeException的綜合例子。
需求:老師用電腦上課;(面向對象思想:名詞提煉法)
老師類,電腦類
可能出現的異常:
電腦藍屏;--->可以解決,重啓電腦,繼續上課;
電腦冒煙;--->老師不能解決,該異常跟老師沒關係,如果繼續往外拋,別的老師也解決不了,此時拋出老師自己的異常,不能上課了;當校長接收到這一異常時可以解決,就是放假休息的,等待電腦修好了繼續上課;
class LanpingException extends Exception {
LanpingException(String msg) {
super(msg);
}
}
class MaoyanException extends RuntimeException {
MaoyanException(String msg) {
super(msg);
}
}
class NoplanException extends Exception {
NoplanException(String msg) {
super(msg);
}
}
class Computer {
private int state = 3;
public void run() throws LanpingException, MaoyanException {
if (state == 1)
System.out.println("電腦運行。。。");
else if (state == 2)
throw new LanpingException("電腦藍屏了。。。請重啓");
else if (state == 3)
throw new MaoyanException("電腦冒煙。。。");
}
public void reset() {
System.out.println("電腦重啓。。。");
}
}
class Teacher {
private Computer cmpt;
private String name;
Teacher(String name) {
this.name = name;
cmpt = new Computer();
}
public void teach() throws NoplanException {
try {
cmpt.run();
} catch (LanpingException e) {
cmpt.reset();
} catch (MaoyanException e) {
test();
System.out.println(e.toString());
throw new NoplanException("無法繼續上課。。。");
}
System.out.println(name + "老師講課。。。");
}
public void test() {
System.out.println("學生做練習。。。");
}
}
class ExceptionExercise {
public static void main(String[] args) {
Teacher t = new Teacher("李");
try {
t.teach();
} catch (NoplanException e) {
System.out.println("全體放假。。。");
}
}
}
總結:前面已經給出了異常處理的幾種代碼的形式,其中finally也是十分重要的一個知識點。finally語句裏面的內容無論什麼情況都會執行;它的應用主要是在數據庫上;因爲數據庫的連接資源是有限的,當發生異常時如果程序停止的話,數據庫的連接如果沒有斷開,將會佔用很多資源,這是finally語句就起到很大的作用。
public void method() throws SQLException {
連接數據庫;
數據庫的操作;
關閉數據庫;//該動作無論數據庫的操作是否成功,都必須進行;
try{
連接數據庫;
數據庫的操作;//throw new SQLException();
}catch(SQLException e) {
...
}finally {
關閉數據庫;
}
}
try...catch的另外幾種格式:
try{
...
}catch(Exception e) {
...
}
try{
...
}catch(Exception e) {
...
}finally {
...
}
try{
...
}finally { //finally用於關閉資源
...
}//記住catch是用於處理異常,如果沒有catch說明此異常沒有被處理過,此時就必須聲明出去;
五,子類覆蓋父類時異常的處理規則:
1,子類在覆蓋父類時如果父類的方法拋出異常,那麼子類的覆蓋方法,只能拋出父類的異常或者該異常的子類。
2,如果在父類方法中拋出多個異常,子類再覆蓋時只能拋出父類異常的子異常,或者不拋。例如:父類方法有A,B,C,D,E這幾個異常,子類再覆蓋該方法時只能拋出這五個異常的子集或者不拋。
3,如果父類或接口的方法中沒有拋出異常,那麼子類在覆蓋該方法時也不可以拋出異常。如果子類方法發生了異常就必須用try語句解決,絕對不能向外拋。
示例分析:
class AException extends Exception {
...
}
class BException extends AException {
...
}
class CException extends Exception{
...
}
class Fu {
public void show() throws AException {
...
}
}
class Zi extends Fu
{
public void show() throws AException {//或者拋出BException,但是絕對不能拋CException,如果發生
//CException那麼就必須在該方法中用try語句處理掉這個異常;
...
}
}
public class ExceptionTestDemon2 {
public static void main(String[] args) {
...
}
}
六,最後給出異常的綜合應用的一個例子來說明異常的應用:
需求:計算一個園和長方形的面積;對於非法值得輸入可以視爲獲取面積出現的問題;問題可以用異常來表示。
//首先自定義一個異常
class FushuException extends RuntimeException {
FushuException(String message) {
super(message);
}
}
//定義一個抽象類,將打印面積的方法放在抽象類中,子類必須重寫該方法
abstract class Area {
public abstract void getArea();
}
//計算矩形面積
class Rect extends Area {
private double width, high;
Rect(double width, double high) {
if (width < 0 || high < 0)
throw new FushuException("長方形的長或寬不能爲零。。。");
// 這裏當然也可以用if語句來判斷然後給出錯誤提示,但是在java中這樣做是不提倡的;
// 因爲這樣寫的話錯誤處理代碼和正常流程代碼聯繫非常緊密,閱讀性比較差;正真做項目時
// 如果用if語句,那麼if裏面將會是一大片處理代碼,閱讀性非常差;而異常處理Exception的
// 好處在於可以將異常處理代碼和正常流程代碼分離開來,當問題發生時,可以不修改正常流程代碼
// 而直接到異常處理代碼區修改異常處理代碼;
this.width = width;
this.high = high;
}
public void getArea() {
System.out.println(width * high);
}
}
//計算圓面積
class Circle extends Area {
private double radious;
public static final double PI = 3.14;
Circle(double radious) {
if (radious < 0)
throw new FushuException("圓半徑爲負數。。。。");
this.radious = radious;
}
public void getArea() {
System.out.println(radious * radious * PI);
}
}
class DoArea {
public void doArea(Area a) {
a.getArea();
}
}
class ExceptionArea {
public static void main(String[] args) {
DoArea d = new DoArea();
d.doArea(new Rect(2.0, 3.6));
d.doArea(new Circle(-2));
}
}