15 異常

配套視頻教程

本文B站配套視頻教程

在使用計算機語言進行項目開發的過程中,即使程序員把代碼寫得盡善盡美,在系統的運行過程中仍然會遇到一些問題,因為很多問題不是靠代碼能夠避免的,比如:客戶輸入數據的格式,讀取文件是否存在,網絡是否始終保持通暢等等。

異常:在Java語言中,將程序執行中發生的不正常情況稱為“異常”。(開發過程中的語法錯誤和邏輯錯誤不是異常)
Java程序在執行過程中所發生的異常事件可分為兩類:

  • Error: Java虛擬機無法解決的嚴重問題。如:JVM系統內部錯誤、資源耗盡等嚴重情況。一般不編寫針對性的代碼進行處理。
  • Exception: 其它因編程錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的代碼進行處理。例如:
    1 空指針訪問
    2 試圖讀取不存在的文件
    3 網絡連接中斷

對于這些錯誤,一般有兩種解決方法:一是遇到錯誤就終止程序的運行。另一種方法是由程序員在編寫程序時,就考慮到錯誤的檢測、錯誤消息的提示,以及錯誤的處理。

捕獲錯誤最理想的是在編譯期間,但有的錯誤只有在運行時才會發生。比如:除數為0,數組下標越界等
分類:編譯時異常和運行時異常

Java異常類層次

image.png

1.運行時異常

  • 是指編譯器不要求強制處置的異常。一般是指編程時的邏輯錯誤,是程序員應該積極避免其出現的異常。java.lang.RuntimeException類及它的子類都是運行時異常。
  • 對于這類異常,可以不作處理,因為這類異常很普遍,若全處理可能會對程序的可讀性和運行效率產生影響。

2.編譯時異常

  • 是指編譯器要求必須處置的異常。即程序在運行時由于外界因素造成的一般性異常。編譯器要求java程序必須捕獲或聲明所有編譯時異常。

常見異常

java.lang.RuntimeException
ClassCastException
ArrayIndexOutOfBoundsException
NullPointerException
ArithmeticException
。。。
java.io.IOExeption
FileNotFoundException
EOFException
java.lang.ClassNotFoundException
java.lang.InterruptedException
java.io.FileNotFoundException
java.sql.SQLException

Java異常舉例

public class Test6_1{
           public static void main(String[] args) {
        String friends[]={"lisa","bily","kessy"};
        for(int i=0;i<5;i++)  {
                System.out.println(friends[i]);   //friends[4]?
            }
        System.out.println("\nthis is the end");
      }
}
程序Test6_1編譯正確,運行結果:java Test6_1
lisa
bily
kessy
java.lang.ArrayIndexOutOfBoundsException
        at Test6_1.main(Test6_1.java:5)
Exception in thread "main"

public class NullRef{
       int i=1;
           public static void main(String[] args) {
        NullRef t=new NullRef();
        t=null;         
        System.out.println(t.i);
      }
}

程序NullRef.java編譯正確,運行結果:java NullRef

java.lang.NullPointerException
    at NullRef.main(NullRef.java:6)
Exception in thread "main" 


public class DivideZero{
       int x;
           public static void main(String[] args) {
        int y;
        DivideZero c=new DivideZero();
        y=3/c.x;        
        System.out.println(“program ends ok!”);
      }
}

程序DivideZero.java編譯正確,運行結果:java DivideZero

java.lang.ArithmeticException: / by zero
    at DivideZero.main(DivideZero.java:6)
Exception in thread "main" 

class Person {
    public static void main(String[] args) {
        Object obj = new Date();
        Person person;
        person = (Person)obj;
        System.out.println(person);
    }
}

程序Person.java編譯正確,運行結果:java Person

java.lang. java.lang.ClassCastException
    at Person.main(Person.java:5)
Exception in thread "main" 

異常處理機制

在編寫程序時,經常要在可能出現錯誤的地方加上檢測的代碼,如進行x/y運算時,要檢測分母為0,數據為空,輸入的不是數據而是字符等。過多的分支會導致程序的代碼加長,可讀性差。因此采用異常機制。

Java異常處理
Java采用異常處理機制,將異常處理的程序代碼集中在一起,與正常的程序代碼分開,使得程序簡潔,并易于維護。

  • Java提供的是異常處理的抓拋模型。
  • Java程序的執行過程中如出現異常,會生成一個異常類對象,該異常對象將被提交給Java運行時系統,這個過程稱為拋出(throw)異常。
  • 異常對象的生成
    由虛擬機自動生成:程序運行過程中,虛擬機檢測到程序發生了問題,如果在當前代碼中沒有找到相應的處理程序,就會在后臺自動創建一個對應異常類的實例對象并拋出——自動拋出

由開發人員手動創建:Exception exception = new ClassCastException();——創建好的異常對象不拋出對程序沒有任何影響,和創建一個普通對象一樣

異常處理是通過try-catch-finally語句實現的。

try{
    ......  //可能產生異常的代碼
}
catch( ExceptionName1 e ){
    ......  //當產生ExceptionName1型異常時的處置措施
}
catch( ExceptionName2 e ){
......  //當產生ExceptionName2型異常時的處置措施
}  
[ finally{
......   //無論是否發生異常,都無條件執行的語句
        }  ]
  • try
    捕獲異常的第一步是用try{…}語句塊選定捕獲異常的范圍,將可能出現異常的代碼放在try語句塊中。
  • catch (Exceptiontype e)
    在catch語句塊中是對異常對象進行處理的代碼。每個try語句塊可以伴隨一個或多個catch語句,用于處理可能產生的不同類型的異常對象。

如果明確知道產生的是何種異常,可以用該異常類作為catch的參數;也可以用其父類作為catch的參數。
比如:可以用ArithmeticException類作為參數的地方,就可以用RuntimeException類作為參數,或者用所有異常的父類Exception類作為參數。但不能是與ArithmeticException類無關的異常,如NullPointerException(catch中的語句將不會執行)。

捕獲異常的有關信息

與其它對象一樣,可以訪問一個異常對象的成員變量或調用它的方法。

getMessage() 獲取異常信息,返回字符串
printStackTrace() 獲取異常類名和異常信息,以及異常出現在程序中的位置。返回值void。

finally

捕獲異常的最后一步是通過finally語句為異常處理提供一個統一的出口,使得在控制流轉到程序的其它部分以前,能夠對程序的狀態作統一的管理。

不論在try代碼塊中是否發生了異常事件,catch語句是否執行,catch語句是否有異常,catch語句中是否有return,finally塊中的語句都會被執行


image.png

異常處理舉例

public class Test6_2{
          public static void main(String[] args)    {
            String friends[]={"lisa","bily","kessy"};
        try {
               for(int i=0;i<5;i++) {
                    System.out.println(friends[i]);
                   }
        } 
        catch(ArrayIndexOutOfBoundsException e)  {
                      System.out.println("index err");
        }
        System.out.println("\nthis is the end");
       }
}

程序Test6_2運行結果:java java6_2
lisa
bily
kessy
index err
this is the end

public class DivideZero1{
       int x;
           public static void main(String[] args) {
        int y;
        DivideZero1 c=new DivideZero1();
        try{
            y=3/c.x;
        }
        catch(ArithmeticException e){       
            System.out.println("divide by zero error!");
        }
        System.out.println("program ends ok!");
      }
}

程序DivideZero1運行結果:java DivideZero1
divide by zero error!
program ends ok!

練習

編寫一個類TestException,在main方法中使用try、catch、finally,要求:
在try塊中,編寫被零除的代碼。
在catch塊中,捕獲被零除所產生的異常,并且打印異常信息
在finally塊中,打印一條語句。

不捕獲異常時的情況

  • 前面使用的異常都是RuntimeException類或是它的子類,這些類的異常的特點是:
    即使沒有使用try和catch捕獲,Java自己也能捕獲,并且編譯通過 ( 但運行時會發生異常使得程序運行終止 )。
    下面代碼,如果不用try-catch處理,循環到第二次就會退出。
public class Main {

    public static void main(String[] args) {
    // write your code here
        Scanner sc = new Scanner(System.in);
        for(int i = 0; i < 5; i++)
        {
            try
            {
                    int k = sc.nextInt();
                    if(i == 1)
                    {
                        k = k / 0;
                    }

                    System.out.println(k);

            }
            catch (Exception ex)
            {
                System.out.println(ex.getMessage());
            }
        }
    }
}
  • 如果拋出的異常是IOException等類型的非運行時異常,則必須捕獲,否則編譯錯誤。也就是說,我們必須處理編譯時異常,將異常進行捕捉,轉化為運行時異常

IOException異常處理舉例

public class Test6_3{
          public static void main(String[] args)    {
        FileInputStream in=new FileInputStream("myfile.txt");
        int b;
        b = in.read();
        while(b!= -1) {
                        System.out.print((char)b);
                    b = in.read();
        }
        in.close();
       }
}

public class Test6_3{
      public static void main(String[] args){
        try{
            FileInputStream in=new FileInputStream("myfile.txt");
            int b;  
            b = in.read();
            while(b!= -1)       {
                System.out.print((char)b);
                b = in.read();
            }
            in.close();
        }catch (IOException e) {
                System.out.println(e);
        }finally {
                System.out.println(" It’s ok!");
        }
      }
}

編譯、運行應用程序Test6_3.java,體會java語言中異常的捕獲和處理機制。
相關知識:FileInputStream類的成員方法read()的功能是每次從相應的(本地為ASCII碼編碼格式)文件中讀取一個字節,并轉換成0~255之間的int型整數返回,到達文件末尾時則返回-1。

聲明拋出異常是Java中處理異常的第二種方式

  • 如果一個方法(中的語句執行時)可能生成某種異常,但是并不能確定如何處理這種異常,則此方法應顯示地聲明拋出異常,表明該方法將不對這些異常進行處理,而由該方法的調用者負責處理。
  • 在方法聲明中用throws語句可以聲明拋出異常的列表,throws后面的異常類型可以是方法中產生的異常類型,也可以是它的父類。

聲明拋出異常舉例:

    public void readFile(String file)  throws FileNotFoundException {
        ……
        // 讀文件的操作可能產生FileNotFoundException類型的異常
        FileInputStream fis = new FileInputStream(file);
         ..……
     }
public class Test6_4{
    public static void main(String[] args){
        Test6_4 t = new Test6_4();
        try{
            t.readFile();
         }catch(IOException e){   }
    }
    public void readFile() throws IOException {
        FileInputStream in=new FileInputStream("myfile.txt");
        int b;  
        b = in.read();
        while(b!= -1)   {
            System.out.print((char)b);
            b = in.read();
        }
        in.close(); 
    }
}

image.png

人工拋出異常

Java異常類對象除在程序執行過程中出現異常時由系統自動生成并拋出,也可根據需要人工創建并拋出。

  • 首先要生成異常類對象,然后通過throw語句實現拋出操作(提交給Java運行環境)。
    IOException e = new IOException();
    throw e;
  • 可以拋出的異常必須是Throwable或其子類的實例。下面的語句在編譯時將會產生語法錯誤:
    throw new String("want to throw");

創建用戶自定義異常類

  • 一般地,用戶自定義異常類都是RuntimeException的子類。
  • 自定義異常類通常需要編寫幾個重載的構造器。
  • 自定義的異常類對象通過throw拋出。
  • 自定義異常最重要的是異常類的名字,當異常出現時,可以根據名字判斷異常類型。

用戶自定義異常類MyException,用于描述數據取值范圍錯誤信息。用戶自己的異常類必須繼承現有的異常類。

class MyException extends Exception {
    static final long serialVersionUID = 1L;
    private int idnumber;
    public MyException(String message, int id) {
        super(message);
        this.idnumber = id;
    } 
    public int getId() {
        return idnumber;
    }
}

public class Test6_5{
        public void regist(int num) throws MyException {
    if (num < 0) 
              throw new MyException(“人數為負值,不合理”, 3);
    else
              System.out.println("登記人數" + num );
        }
        public void manager() {
    try {
               regist(100);
    } catch (MyException e) {
               System.out.print("登記失敗,出錯種類"+e.getId());     
    }
    System.out.print("本次登記操作結束");
       }
       public static void main(String args[]){
    Test6_5 t = new Test6_5();
    t.manager();
       }
}

image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容