狠狠躁夜夜躁人人爽超碰97香蕉|色婷婷日日躁夜夜躁|亚洲一区欧美一区在线播|久久久久久性高|伊人久久大香线蕉亚洲

歡迎來到同城快修-附近家電維修、家電清洗、家電安裝服務平臺

24小時家電維修熱線:

400—1558638

當前位置:主頁 > 空調 > 維修資訊 >

邏輯故障(邏輯故障分為哪兩種)

發布日期:2023-01-12 15:47:58 瀏覽:
邏輯故障(邏輯故障分為哪兩種)

前沿拓展:

邏輯故障

我看了下鍋爐的水位計邏輯故障怎么去處理,不了解。

不過一般水位計出現假水位。還有水位怱上忽下。看不清水等。



d. 異常

內容導視:

異常分類異常處理自定義異常調試

照例,求職,簡歷還未刷新。

鏡子中的我,被大象牽著的我,假我面目可憎。我仿佛預見到這幕場景發生過無數次了,但沒能阻止它,一次失敗的多次疊加就能忽略并顛覆我過往日夜的許下的每一個決心?偽啊!

d.1 異常分類

內容導視:

RuntimeExceptionException

程序的運行并不總是一帆風順的,可能讀取的文件不存在、讀取的不是個文件、使用值為 null 的引用訪問實例變量、訪問不存在的下標、強行將字符串轉成整數...這些不正常的情況稱為異常。(語法錯誤不是異常)

運行時發生異常(拋出了異常對象),如果不處理,默認退出程序,異常后面的代碼不會執行。

int i = 5 / 0; System.out.println("后面的代碼不再執行..."); Exception in thread "main" java.lang.ArithmeticException: / by zero at com.cqh.Excep.main(Test.java:1)

所有異常類都是 Throwable 的子類,體系圖:

d.1.1 Error

Error:JVM 無法解決的嚴重錯誤,如系統內部錯誤、資源耗盡等錯誤,如: StackOverflowError(棧溢出)和 OutOfMemoryError(內存溢出);程序會崩潰。

應用程序不應該拋出這種類型的對象。 如果出現了這樣的內部錯誤,除了通告給用戶,并盡力使程序安全地終止之外, 再也無能為力了。

這種情況很少出現,大多數情況下我們只關注于 Exception。

如果線程請求的棧深度大于虛擬機所允許的深度,將會拋出 StackOverflowError 異常;如果虛擬機棧可以動態擴展時卻無法申請到足夠的內存,就會拋出 OutOfMemoryError 異常。

d.1.2 RuntimeException

Exception:因為邏輯錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的代碼處理。

Exception 分為兩大類:編譯時異常、運行時異常。

運行時異常又稱非受檢異常,不要求強制處理;RuntimeException(包括子類)是運行時異常。

通常是由于程序邏輯錯誤導致的異常(內因),如空指針、除 0、下標越界、類型轉換、數字格式化等運行時異常。

這類異常如果強制要求處理,比如計算、調用方法、訪問字段、訪問數組某個下標的元素等,都需要 try catch,那么代碼量會急速膨脹,看著就很混亂。

這類異常從一開始就不該存在,應極力避免,可以提早檢查出來;使用 try catch 處理這類異常有些小題大作,效率也低。

例:

obj.method();

應考慮到 obj 為 null 的情況,避免空指針異常:(如果能確定 obj 一定不為 null,則不需要處理)

if (obj != null) { obj.method(); }

不要使用 try catch:(無法通過預檢查的異常除外,比如 NumberFormatException)

try { obj.method(); } catch (NullPointerException e) { e.printStackTrace(); }

常見的運行時異常

1.java.lang.NullPointerException 空指針異常

Object obj = null; obj.getClass();

2.java.lang.ArithmeticException 算數異常

public static void main(String[] args) { double num = p(5, 0); } private static double p(int pidend, int pisor) { return pidend / pisor; }

3.java.lang.ArrayIndexOutOfBoundsException 數組下標越界異常

int[] arr = {6, 2, 7}; System.out.println(arr[9]);

4.java.lang.ClassCastException 類型轉換異常

Object obj = new ArrayList(); String str = (String)obj;

5.java.lang.NumberFormatException 數字格式化異常

Integer i = Integer.valueOf("不是數字");d.1.3 Exception

除 RuntimeException 外的 Exception 是編譯時異常,又稱受檢異常,必須手動處理,否則無法通過編譯。

FileInputStream fis = new FileInputStream("E:\\a.txt");java: 未報告的異常錯誤java.io.FileNotFoundException; 必須對其進行捕獲或聲明以便拋出

這類異常,并不是程序本身邏輯有問題,而是取決于外部環境,并不取決于代碼,如 E 盤下的 a.txt 是否存在。

我們無法控制這些外在因素,為此需要手動處理 FileNotFoundException,說明當此文件不存在時應該怎么辦。

try { FileInputStream fis = new FileInputStream("E:\\a.txt");} catch(FileNotFoundException e) { System.out.println("a.txt 不存在,我們應該...");}d.2 異常處理

內容導視:

throwstry catch

對于編譯時異常必須處理,否則通不過編譯;處理異常的方式有兩種,throws 上拋給調用者,當前方法終止運行;或是 try catch 處理異常,程序繼續運行。

d.2.1 throws

當方法中的語句可能拋出某個異常時,但是不確定如何處理這種異常,則此方法應顯示地聲明拋出異常,表明該方法將不對這些異常進行處理,而由該方法的調用者負責處理:try catch 或繼續上拋。

在方法聲明中用 throws 語句聲明拋出異常的列表,throws 后面的異常類型可以是方法中可能拋出的異常類型,也可以是它的父類。如把 FileNotFoundException 換成 Exception,即它的父類。

語法:

方法聲明 throws 異常1, 異常2 ... {}

表明此方法可能拋出 FileNotFoundException 異常

public static void some() throws FileNotFoundException { // 此處可能會有異常拋出,當這句拋出異常時,后面的代碼不會執行 FileInputStream fis = new FileInputStream("E:\\a.txt"); ...}

調用者必須處理此編譯時異常,是繼續上拋,還是 try catch 自己處理。

public static void main(String[] args) throws FileNotFoundException { // 此處可能會有異常拋出,當這句拋出異常后,后面的代碼不會執行 some(); ...}

throws 過程:

發生異常后,異常后的代碼不會執行,直接結束方法回到調用者處,由調用者決定如何處理此異常。如果 main 方法自己不處理異常,而是繼續上拋給 JVM,異常后的代碼不會執行;JVM 會調用異常對象的 printStackTrace 方法打印出異常信息,然后退出程序。

運行時異常默認以 throws 的方式處理(隱式)。

public void some() throws RuntimeException { int a = 5 / 0;}// 等同于public void some() { int a = 5 / 0;}

子類重寫父類的方法時,對拋出異常的規定:子類重寫的方法,所拋出的異常類型要么和父類拋出的異常一致,要么為父類拋出的異常類型的子類型(或者干脆不顯示拋出異常)。這是為了多態,父類出現的地方可以使用子類替換,且不會出現異常或錯誤。

class Animal { public void run() {}}// 任意子類class ? extends Animal { // 假如這里可以通過編譯,不受限制地拋出異常 public void run() throws Exception {}}class Test { // 這里不確定傳入哪個子類對象 public static void run(Animal a) { /* 編譯時,看編譯類型的此方法沒有拋出異常,正常通過; 運行時,調用的是子類重寫后的方法,但子類重寫后的方法如果拋出了編譯時異常 但這里又沒有處理,或者告知此方法可能會拋出異常(throws 或 try catch)相當于繞過了編譯器的檢查,開發者很可能就漏掉了對此異常的處理,導致程序崩潰 */ a.run(); }}

所以經常看到父類的方法聲明拋出異常,而實際沒有拋出異常,強行要求處理,是預見到重寫后的方法很有可能拋出異常。

d.2.2 try catch

try 包含可能出錯的代碼,當異常發生時由對應的 catch 處理。

語法:

try {// 可能會出現異常的代碼 } catch (異常類型 變量名) {// 發生異常后應執行的代碼 }

執行 try catch 語句過程:

執行 try {} 中的語句如果沒有拋出異常,catch {} 中語句不會執行(直至 try 中語句執行結束,整個 try catch 結束,程序繼續運行)如果拋出了異常,異常處后面的語句不會執行,直接執行對應 catch 中的語句(直至 catch 語句執行結束,整個 try catch 語句結束,程序繼續運行)Random random = new Random();int b = random.nextInt(2);try { int a = 3; System.out.println("中"); int c = a / b; System.out.println("后");} catch (ArithmeticException e) { System.out.println("前");}

當 b != 0 時,順利執行完 try 中的代碼,輸出

當 b = 0 時,輸出 ,執行到 int c = a / b; 這行拋出異常,跳轉至對應 catch 塊,輸出

異常并不是越早 try catch 越好,當調用者已做好準備,執行某段代碼出現異常情況后該如何處理,結果調用的方法內部自己 try catch 了異常,沒有上拋,但其實根本沒有處理好,也沒有打印異常棧軌跡信息,外部調用者沒有接收到拋出的異常,以為程序還正常運行,沒有啟動應急方案,這些無意被忽略的異常也許會釀成大禍,直到很久后才被發現。

我無意就犯這樣的錯,有時候爆紅強迫我處理異常,我快捷鍵隨便一按,默認 try catch 處理,便編寫其它地方的代碼,正好埋下了隱患,但察覺不了,程序還能繼續運行,直到要使用數據時,才發現不對勁,疑惑不是已經寫了 catch 處理嗎?debug 一步步調試,原來被內部方法自己處理掉了,外部的 catch 沒有生效,此時程序都不知道跑了多遠了...

d.2.3 多個 catch 塊

當 try 包圍的代碼內拋出了不止一個異常,需要多個 catch 子句,用于捕獲不同的異常,要求:父類異常必須在子類異常的后面、無法捕捉不可能拋出的編譯時異常。

發生異常后,從上至下選擇對應的 catch 子句執行,只會執行一條 catch 分支 。(重復捕捉沒有意義)

try { FileInputStream fis = new FileInputStream("E:/a.txt"); int i = 5 / 0;} catch (FileNotFoundException e) { System.out.println("文件沒有找到");} catch (RuntimeException e) { System.out.println("出現了運行時異常");} catch (Exception e) { System.out.println("出現了其它編譯時異常");}

如果把父類 Exception 放在前面,發生的子類型異常一定會被此 catch 捕捉到,后面捕捉子類型異常 FileNotFoundException 的 catch 永遠不會執行,沒有意義。

沒有捕捉到的運行時異常會拋給上一級調用者;不捕捉的編譯時異常必須以 throws 的方式處理。

try { int b = 5 / 0;} catch (NullPointerException e) { // 打印異常信息 e.printStackTrace();}

如果多個異常同一種處理方式,除了使用父類型的異常囊括統一處理,還可以在 catch 中將不同類型的異常以 | 分隔,統一處理。(被分隔的異常不得有父子關系)

JDK7 新增特性:multi-catch 多重捕獲機制

try { int i = 5 / 0; FileInputStream fis = new FileInputStream("E:/a.txt"); DriverManager.getConnection(null);} catch (RuntimeException | FileNotFoundException | SQLException e) { System.out.println("異常已發生");}d.2.4 finally

始終會執行的代碼可以放入 finally 中;

// 如果將 fis 定義在 try 內,則 finally 內無法訪問到此變量FileInputStream fis = null;try { fis = new FileInputStream("E:\\a.txt");} catch (FileNotFoundException e) { e.printStackTrace();} finally { // 如果 fis 不等于 null,就調用 close 方法關閉資源 if (fis != null) { try { // 此方法也會拋出異常,所以需要 try catch 處理 fis.close(); } catch (IOException e) { e.printStackTrace(); } }}

執行 try 語句時,不管異常是否發生,都會執行 finally 中的代碼(釋放資源、關閉連接),除非退出程序或被打斷。

強迫處理異常的確非常繁瑣,當確定文件一定存在時,本應是如下語句,現在倒是變得不那么簡單清晰明了。

FileInputStream fis = new FileInputStream("E:\\a.txt");fis.close();

如果沒有出現異常,則執行 try 塊中所有語句,不執行 catch 塊中語句;如果有 finally,最后還需要執行 finally 里面的語句。

如果出現異常,則 try 塊中異常發生后,剩下的語句不再執行。將執行 catch 塊中的語句;如果有 finally,最后還需要執行 finally 里面的語句。

finally 語句塊一定執行。(除非 try 語句塊沒執行,或 System.exit 退出程序)

有人就問了,就算異常發生,異常都被 catch 捕捉了,有沒有 finally 不都會繼續執行嗎?

FileInputStream fis = null;try { fis = new FileInputStream("E:\\a.txt");} catch (FileNotFoundException e) { e.printStackTrace();} // 應繼續執行if (fis != null) {// 避免空指針異常 try { fis.close(); } catch (IOException e) { e.printStackTrace(); }}

不一定,如下例執行到 return; 語句,整個方法就結束了,不會執行剩下的語句。

try { int i = 5; return;} catch (RuntimeException e) { e.printStackTrace();}System.out.println("請執行我");

但是如果放入 finally 中:

try { int i = 5; return;} finally { System.out.println("請執行我");}

在 return 前,會先執行 finally 中代碼,輸出 請執行我

try finally 配合使用,這種用法相當于沒有捕獲異常,需要上拋給調用者處理。不管是否發生異常,都必須執行某段代碼。

d.2.5 try with resources

JDK7 新增特性,用于簡化關閉流的過程;不需要在 finally 塊中關閉流了,try 語句結束后會自動調用對象的 close 方法。(若 try 中拋出了異常,try 剩余代碼不再執行,在進入 catch 之前先關閉流)

// 在 try () 中放入需要關閉資源的對象(實現了 AutoCloseable 接口的實現類對象)// 可以放置多個對象,以 ; 分隔try (FileInputStream fis = new FileInputStream("E:\\a.txt")) {} catch (FileNotFoundException e) { e.printStackTrace();} catch (IOException e) { // 關閉流(調用 close 方法)時如果拋出了異常,由此 catch 塊處理 e.printStackTrace();}

JDK7 需要在 try 括號中需要聲明變量,如 try (FileInputStream fis = f),JDK9 后可以直接放入,如 try (f)。

如果不想處理 try 中拋出的異常,但需要關閉資源:

public static void some() throws FileNotFoundException { FileInputStream f = null; try { f = new FileInputStream("E:\\a.txt"); } finally { try (FileInputStream f1 = f) { } catch (Exception e) { e.printStackTrace(); } }}

也不想處理 close 方法拋出的異常:

public static void some() throws IOException { try (FileInputStream f = new FileInputStream("E:\\a.txt")) { ... }}

或者拋出異常,但不想強制調用者必須處理異常,可以轉為運行時異常:

public static void some() throws IOException { try (FileInputStream f = new FileInputStream("E:\\a.txt")) { ... } catch (IOException e) { throw new RuntimeException("invoke failed", e); }}

調用 close 方法的順序

class Test { public static void main(String[] args) { try (MyStream m1 = new MyStream("s1"); MyStream m2 = new MyStream("s2"); MyStream m3 = new MyStream("s3")) { throw new Exception("run"); } catch (Exception e) { e.printStackTrace(); } }}class MyStream implements AutoCloseable { private String name; public MyStream(String name) { this.name = name; System.out.println("creating " + name); } @Override public void close() throws Exception { /*if (true) { throw new Exception(name); }*/ System.out.println("closing" + name); }}

運行:

creating s1creating s2creating s3closings3closings2closings1java.lang.Exception: runat com.cqh.cover.Test.main(OverrideException.java:22)

與聲明順序相反,逆序調用對象的 close 方法。

創建對象時拋出異常

期間某個對象調用構造器時拋出了異常,不再創建剩下的對象,逆序調用已創建好的對象的 close 方法,try 語句塊結束。

使得創建 m3 對象時拋出異常:

try (MyStream m1 = new MyStream("s1"); MyStream m2 = new MyStream("s2"); MyStream m3 = new MyStream("s3"); MyStream m4 = new MyStream("s4")) { System.out.println("try 中的語句不執行");} catch (Exception e) { System.out.println(e);}

運行:

creating s1creating s2creating s3closings2closings1java.lang.RuntimeException: s3 constructor failed

拋出多個異常

將 close 方法的注釋打開,注釋 try 中拋出的異常。

運行:

creating s1creating s2creating s3java.lang.Exception: s3 // 這是 m3 調用 close 方法時拋出的異常at com.cqh.cover.MyStream.close(OverrideException.java:39)at com.cqh.cover.Test.main(OverrideException.java:23)Suppressed: java.lang.Exception: s2... 2 moreSuppressed: java.lang.Exception: s1... 2 more

調用 m3 的 close 方法時拋出了異常,接著調用 m2、m1 的 close 方法也拋出了異常,為了避免異常信息丟失,先拋出的異常會調用 addSuppressed 方法,添加被抑制的異常。

將 try 中的注釋打開,運行:

creating s1creating s2creating s3java.lang.Exception: run // try 中拋出的異常,下面三個是被抑制的異常的信息at com.cqh.cover.Test.main(OverrideException.java:22) Suppressed: java.lang.Exception: s3at com.cqh.cover.MyStream.close(OverrideException.java:39)at com.cqh.cover.Test.main(OverrideException.java:23) Suppressed: java.lang.Exception: s2at com.cqh.cover.MyStream.close(OverrideException.java:39)at com.cqh.cover.Test.main(OverrideException.java:23) Suppressed: java.lang.Exception: s1at com.cqh.cover.MyStream.close(OverrideException.java:39)at com.cqh.cover.Test.main(OverrideException.java:23)d.2.6 被吞掉的異常

不調用 addSuppressed 方法添加被抑制的異常

始終會執行的 finally 塊有時候會覆蓋掉 try 中的異常:

try {throw new RuntimeException("try:throw Exception"); } finally { throw new RuntimeException("finally:throw Exception"); }

運行:

// finally 中拋出的異常Exception in thread "main" java.lang.RuntimeException: finally:throw Exceptionat com.cqh.cover.Test.main(OverrideException.java:22)

try 拋出的異常還沒有處理,接著又拋出了一個異常,try 異常信息丟失,只記錄下了 “finally:throw Exception”。

改進

RuntimeException re = null;try { throw new RuntimeException("try:throw Exception"); } catch (RuntimeException e) { re = e; // 拋出 try 中的異常 throw re;} finally { try { throw new RuntimeException("finally:throw Exception"); } catch (RuntimeException e) { // 如果 try 中沒有拋出異常,直接拋出 finally 內的異常 if (re == null) { throw e; } else { // 否則 try 拋出的異常添加 e 到被抑制的異常列表中 re.addSuppressed(e); } }}

運行:

// try 中拋出的異常Exception in thread "main" java.lang.RuntimeException: try:throw Exceptionat com.cqh.cover.Test.main(OverrideException.java:21) // finally 中被抑制的異常Suppressed: java.lang.RuntimeException: finally:throw Exceptionat com.cqh.cover.Test.main(OverrideException.java:27)

這樣十分麻煩,所以還是使用 try with resources 語句方便,能自動 addSuppressed 異常,不需要我們操心。

d.3 自定義異常

內容導視:

自定義異常類步驟拋出自定義異常常用方法d.3.1 自定義異常類步驟

當程序出現了某些錯誤,如果內置的異常類無法滿足需求,可以自定義異常,描述此錯誤信息。

步驟:

定義類,編譯時異常繼承 Exception,運行時異常繼承 RuntimeException調用父類的有參構造,以傳遞錯誤信息public class UserNameFormatException extends Exception { public UserNameFormatException(String message) { super(message); }}d.3.2 拋出異常

在你認為發生錯誤的地方拋出此異常對象,throw new 異常類("錯誤原因");

如果拋出的是編譯時異常,需要在方法處使用 throws 聲明可能會發生的異常。

public User getUserByName(String name) throws UserNameFormatException { if ("xxx".equals(name)) { throw new UserNameFormatException("用戶名稱非法!"); } ...}

throw 語句只能放在 {} 中的最后一句,拋出異常后,方法直接結束,返回上一級,由調用者處理此異常。

伙伴們,注意一下,new 別掉了。

d.3.3 常用方法

構造器(String message, Throwable cause):錯誤信息,異常發生的原因。

當捕獲了異常后,再次拋出新異常時(目的是改變異常類型,比如當前方法不支持拋出編譯時異常、忽略細節等原因),如果不希望丟失原始異常的信息,使用此構造;或者調用 initCause 方法。

try { // 可能會拋出的異常 ...} catch (FileNotFoundException e) { // 拋出此異常的原因是 try 中拋出了 FileNotFoundException throw new IOException("讀寫文件失敗!", e);}

void addSuppressed(Throwable exception):添加被抑制的異常。

如果由調用者處理異常;在 try 塊中拋出了異常后,在返回至調用者前,會執行 finally 塊中的語句;但是 finally 塊在執行的過程中,也可能會拋出異常。

如果 finally 塊也拋出了異常,則此異常被返回給調用者,但之前 try 塊中的異常就丟失了。

如下例,try 拋出的算術異常,就被空指針異常覆蓋了,控制臺上只會打印空指針異常。

try { int i = 5 / 0;} finally { String str = null; str.length();}

同理,不要在 finally 中使用 return 語句,會忽略 try 中的異常或返回語句。

try { throw new Exception("被忽略的異常");} finally { return;}

當 try 中出現的異常才是問題的根源,可以忽略 finally 中的異常(抑制此異常)。

如下例,但是感覺還是挺麻煩:

// 準備拋出的異常RuntimeException cue = null;try { int i = 5 / 0;} catch (ArithmeticException a) {cue = a;} finally { try { String str = null; str.length(); } catch (NullPointerException n) { /* 只有當準備拋出的異常為 null 時(說明 try 中沒有異常) 才記錄 finally 中的異常 */ if (cue == null) { cue = n; } } // 拋出異常,由調用者處理 if (cue != null) { throw cue; }}RuntimeException cue = null;try { int i = 5 / 0;} catch (ArithmeticException a) {cue = a; throw cue;} finally { try { String str = null; str.length(); } catch (NullPointerException n) { // 只有 cue 為 null 時才拋出 finally 內的異常 if (cue == null) { throw n; } }}

如果不想丟失異常信息,使用 addSuppressed 方法,添加被抑制的異常。

RuntimeException cue = null;try { int i = 5 / 0;} catch (ArithmeticException a) {cue = a;} finally { try { String str = null; str.length(); } catch (NullPointerException n) { if (cue == null) { cue = n; } else { // 添加被抑制的異常 cue.addSuppressed(n); } } if (cue != null) { throw cue; }}RuntimeException cue = null;try { int i = 5 / 0;} catch (ArithmeticException a) { cue = a; throw cue;} finally { try { String str = null; str.length(); } catch (NullPointerException n) { if (cue == null) { throw n; } else { cue.addSuppressed(n); } }}

使用 try-with-resources 時,當 try 中拋出異常,自動調用 close 方法也拋出了異常,會抑制 close 方法拋出的異常,同時調用 addSuppressed 添加至原有異常,不想像上例這么麻煩,可以使用此特性。

Throwable getCause():獲取原始異常(本異常發生的原因)

String getMessage():獲取錯誤信息

StackTraceElement[] getStackTrace():獲取棧軌跡元素數組,可以據此定位異常位置;默認開啟,可以關閉此功能;

在創建異常對象時,調用如下構造器,writableStackTrace 賦值為 false,就不會調用 fillInStackTrace 方法記錄棧軌跡,即有關當前線程的棧幀的當前狀態的信息;此時調用 getStackTrace 方法,返回的數組沒有元素。

構造器(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)

棧軌跡(stack trace)是一個方法調用過程的列表,記錄了拋出異常的方法以及所在行數。構造器中默認調用 fillInStackTrace 記錄棧軌跡,可以手動調用此方法更新異常拋出點的調用棧信息,將之前的信息丟棄。

...57 } catch (IOException | NoSuchFieldException | IllegalAccessException e) {58 throw e;59 }java.lang.NoSuchFieldException: iat com.cqh.scanner.TestScanner.getFieldValue(TestScanner.java:35)at com.cqh.scanner.TestScanner.getHandle(TestScanner.java:54)at com.cqh.scanner.TestScanner.main(TestScanner.java:69)

不想丟失棧信息,可以將原有異常作為新異常的 cause,再拋出新異常;或者先調用 e.printStackTrace 方法打印原有異常信息。

...57 } catch (IOException | NoSuchFieldException | IllegalAccessException e) {58 e.fillInStackTrace();59 throw e;60 }java.lang.NoSuchFieldException: iat com.cqh.scanner.TestScanner.getHandle(TestScanner.java:58)at com.cqh.scanner.TestScanner.main(TestScanner.java:70)

Throwable[] getSuppressed():獲取被抑制的異常;默認開啟,可以關閉此功能,enableSuppression 設置為 false。

Throwable initCause(Throwable cause):設置異常發生的原因。

void printStackTrace():使用 System.err 標準錯誤輸出流打印異常信息,如異常類型、錯誤信息、原因、棧軌跡、被抑制的異常等信息。(可以傳入 PrintStream 類型的對象,指定輸出位置)

String toString():獲取異常類型及錯誤信息。

d.4 調試

內容導視:

單元測試assert日志d.4.1 單元測試

編寫了多個方法,我需要驗證它們的可行性;于是編寫一個測試類,最開始在 main 方法多次調用方法1,傳入不同的參數,使用 sout 將結果打印在控制臺上,運行程序,肉眼辨別結果是否是預期值;不是則在方法1多處插入 sout 語句,觀察究竟是哪里出了問題,或者 debug;好,發現錯誤處修改后,再手動將 sout 語句一個個刪掉。

好,方法1驗證完畢,因為只有一個入口方法,只能將 main 方法中的測試代碼清空,繼續測試方法2、方法3...

這時突然想起測試方法1時,忽略了某處細節,又將 main 方法清空,憑著回憶復原測試代碼,如此反復...

的確很麻煩,不是嗎?使用單元測試,每個方法都可單獨運行,不需要在 main 方法內反復修改。

IDEA 在 Project Structure 的模塊下添加 Dependencies,導入 junit-4.13.1、hamcrest-all-1.3 的 jar 包,jar 包可以在 Maven 倉庫中搜索并下載:https://mvnrepository.com/

然后創建 src 的同級目錄 test,右鍵此目錄 Mark Directory as/Test Sources Root,使之變綠;

然后在 test 目錄下編寫測試類,包名與被測試類所在包一致,在方法上添加注解 @Test;(import org.junit.Test;)

如果是 Maven 項目,測試類放在 src/test/java 下。

想要運行哪個方法就點擊綠色三角(或 Ctrl + Shift + F10),點擊類左邊的三角運行所有方法。

規則:

公開的類公開的實例方法,沒有返回值

規范:

類名為 “測試的類名 + Test”方法名為 “測試的方法名 + Test”

下面是常見的測試注解:

@Test:把一個方法標記為測試方法@Before:每一個測試方法執行前自動調用一次@After:每一個測試方法執行完自動調用一次@BeforeClass:所有測試方法執行前執行一次,在測試類還沒有實例化就已經被加載,所以用 static 修飾@AfterClass:所有測試方法執行完執行一次,在測試類還沒有實例化就已經被加載,所以用 static 修飾@Ignore:暫不執行該測試方法d.4.2 assert

剛剛圖中方法內的 assert 關鍵字是斷言,JDK4 新特性,在測試階段時使用,保證程序的正確性。

語法:assert 布爾值;assert 布爾值 : "錯誤信息";

Object result = method();assert result == 預期結果;

如果布爾值為 false,將會拋出 AssertionError 異常,不再需要 sout 輸出,通過人眼驗證結果。

在測試類中可以使用 Assert.assertEquals("預期結果", "實際結果"); 代替。

斷言默認關閉,可以運行時加入 -enableassertions-ea 參數,啟動斷言。

打開 Edit Configurations。

在 VM options 框內輸入 -ea 即可,默認開啟所有類的斷言,可以使用 -ea:完整類名 只啟動某個類中的斷言。

如果沒有 VM options 框,點擊 Modify options,添加 Add VM options。

開啟斷言后,可以使用 -da:完整類名-disableassertions:完整類名 禁用某個類中的斷言。

d.4.3 日志

不是則在方法1多處插入 sout 語句,觀察究竟是哪里出了問題,或者 debug;好,發現錯誤處修改后,再手動將 sout 語句一個個刪掉。

這樣做比較麻煩,使用日志記錄器記錄結果,打開記錄或關閉都比較方便。

日志級別,從高到低分為如下級別:

SEVERE (highest value):嚴重WARNING:警告INFO:信息CONFIG:配置FINE:詳細FINER:較詳細FINEST (lowest value):非常詳細

Logger 類中:

Logger getGlobal():獲取全局記錄器

Logger getLogger(String name):創建給定名稱的記錄器,一般為包名,若有此名稱的記錄器,則直接返回

String getName():獲取記錄器的名稱

void log(Level level, String msg):記錄某級別的消息

void log(Level level, String msg, Throwable thrown):記錄異常信息

void logp(Level level, String sourceClass, String sourceMethod, String msg):記錄某級別的消息,指定所在類和方法名。

void config(String msg):記錄 config 級別的消息

void info(String msg):記錄 info 級別的消息

void severe(String msg):記錄 severe 級別的消息

void setLevel(Level newLevel):設置日志級別,指定此記錄器將記錄哪些消息級別。低于此值的消息級別將被丟棄。級別值 Level.OFF 可用于關閉日志記錄,級別值 Level.ALL 打開所有級別的日志記錄;

如果 newLevel = null 且有父記錄器,設置為父記錄器的級別;默認為 INFO 級別;

若子記錄器沒有設置級別,修改父記錄器的級別同時會修改子記錄器的級別。

void setParent(Logger parent):設置 parent 為此記錄器的父記錄器,若此記錄器沒有設置級別,級別設置為父記錄器的級別。

// 默認級別為 info,以下級別的消息不會顯示Logger global = Logger.getGlobal();// 會顯示global.info("info 級別的消息");// 不會顯示global.config("config 級別的消息");// 設置日志級別為 SEVERE,以下級別的消息不會顯示global.setLevel(Level.SEVERE);// 會顯示global.severe("severe 級別的消息");// 不會顯示global.info("info 級別的消息2");

你可能發現修改了級別,控制臺上卻沒有顯示,打開 JDK 安裝目錄下的 jre/lib/logging.properties 日志記錄配置文件,修改 java.util.logging.ConsoleHandler.level = FINEST

############################################################# 使用指定的日志記錄配置文件# 運行時添加選項:-Djava.util.logging.config.file=配置文件的絕對路徑############################################################handlers= java.util.logging.ConsoleHandler# 默認是控制臺處理器,負責將消息打印在控制臺上# 可以換成 java.util.logging.FileHandler,消息會輸出在指定文件中# 多個處理器使用 , 分隔.level= INFO# 日志記錄器的默認級別為 INFO,低于此級別的消息會被丟棄,代碼中可以使用 setLevel 方法設置級別# 指定日志記錄器的默認級別為 INFO,此配置文件添加一句# 日志記錄器名.level=INFOjava.util.logging.FileHandler.pattern = %h/java%u.log# 若有 FileHandler,會將消息輸出到 %h/java%u.log,可以改為其它路徑# 注意不要含有中文,確保目錄存在# %h,用戶的主目錄,可以通過 System.getProperty("user.home") 獲取# %u,是防止文件重名的編號# 在 Windows 系統下,此日志文件路徑為 C:\Users\用戶名\java?.logjava.util.logging.FileHandler.limit = 50000# 日志文件最大字節數,改 0 代表無限制# 如果日志文件個數為 1,超過 50 kb 后就會覆蓋原有文件內容# 如果日志文件有多個,超過 50 kb 后,將第一個文件內容移到下一個文件,依次類推# 最后將最新日志輸出到第一個文件中java.util.logging.FileHandler.count = 1# 日志文件個數為 1# 如果只有單個文件,每次運行內容都會被覆蓋# 如果有多個日志文件,每次運行輸出內容到第一個文件,第一個文件原有內容轉移到第二個文件中# 依次轉移,直到最后一個文件內容被倒數第二個文件內容覆蓋java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter# 默認以 xml 格式保存,可以改為 java.util.logging.SimpleFormatter 以普通文本格式保存# 添加如下屬性,每次運行時追加日志到第一個文件尾部# java.util.logging.FileHandler.append = truejava.util.logging.ConsoleHandler.level = INFO# 控制臺上打印的消息限制在 INFO 以上,低于此級別的消息不會在控制臺上輸出java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter# 以普通文本格式輸出在控制臺上d.x 總結回顧

運行時異常應以預檢查的方式避免,用 try catch 處理,效率太低。

編譯時異常不確定如何處理或讓調用者處理就 throws 上拋,否則就 try catch 處理。

throws 與 throw 的區別

關鍵字

作用

位置

所跟

意義

throws

異常處理的一種方式

方法聲明處

可能拋出的異常類型

表明自己不處理異常,交給調用者處理

throw

拋出異常對象的關鍵字

方法體中

異常對象

說明程序某處出現了錯誤

d.y 課后習題

d.1 輸出?

1)

public static void main(String[] args) { some(); System.out.println("main...");}private static void some() { try { System.out.println("try..."); System.out.println(3 / 0); } catch (Exception e) { } finally { System.out.println(3 / 0); System.out.println("finally..."); }}

2)

public static void main(String[] args) { System.out.println(some());}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return 2; } finally { return 3; }}

3)

private static int i = 1;public static void main(String[] args) { System.out.println(some()); System.out.println(i);}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return i; } finally { i++; }}

4)

private static int i = 1;public static void main(String[] args) { int j = some(); System.out.println(j); System.out.println(i);}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return i++; } finally { return i++; }}

5)

public static void main(String[] args) { int j = some(); System.out.println(j);}private static int some() { try { System.exit(0); return 1; } catch (Exception e) { return 2; } finally { System.out.println("finally..."); return 3; }}

6)

private static int i = 1;public static void main(String[] args) { int j = some(); System.out.println(j); System.out.println(i);}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return i++; } finally { i++; }}

7)

public static void main(String[] args) { System.out.println(some());}private static int some() { try { String[] strArr = new String[3]; if(strArr[2].equals("tom")){ System.out.println("try..."); } else { strArr[3] = "jerry"; } return 1; } catch (ArrayIndexOutOfBoundsException e) { return 2; } catch (NullPointerException e){ return 3; } finally { return 4; }}

8)

public static void method1() { try { System.out.println("進入方法1"); throw new RuntimeException("異常1"); } finally { System.out.println("方法1的finally"); }}public static void method2() { try { System.out.println("進入方法2"); return; } finally { System.out.println("方法2的finally"); }}public static void main(String[] args) { try { method1(); } catch (Exception e) { System.out.println(e.getMessage()); } method2();}

9)

public static void func() { try { throw new RuntimeException(); } finally { System.out.println("B"); }}public static void main(String[] args) { try { func(); System.out.println("A"); } catch (Exception e) { System.out.println("C"); } System.out.println("D");}

d.2 鍵盤輸入得到整數后輸出到控制臺,若不是整數,循環輸入,直到得到整數為止。

d.y 習題答案

d.1 輸出?

1)

public static void main(String[] args) { some(); System.out.println("main...");}private static void some() { try { System.out.println("try..."); System.out.println(3 / 0); } catch (Exception e) { } finally { System.out.println(3 / 0); System.out.println("finally..."); }}

輸出 try... 后拋出異常,進入 catch 塊,最后執行 finally 塊的語句,上拋異常到 main 方法,繼續上拋,由 JVM 打印異常信息,退出程序。

2)

public static void main(String[] args) { System.out.println(some());}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return 2; } finally { return 3; }}

拋出算術異常,到 catch 語句塊,在返回 2 前執行 finally 語句,返回 3。

3)

private static int i = 1;public static void main(String[] args) { System.out.println(some()); System.out.println(i);}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return i; } finally { i++; }}

執行 try 子句時拋出算術異常,被 catch 塊捕捉,將 i 的值存入操作數棧,執行 finally 子句,i++ 為 2,再返回操作數棧頂值 1。

輸出 12

4)

private static int i = 1;public static void main(String[] args) { int j = some(); System.out.println(j); System.out.println(i);}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return i++; } finally { return i++; }}

進入 catch 子句,此時 i = 1,1 存入操作數棧;后 i++ 為 2,執行 finally 子句,此時 i 為 2,2 存入操作數棧,后 i++ 為 3;返回棧頂數 2。

輸出 23

5)

public static void main(String[] args) { int j = some(); System.out.println(j);}private static int some() { try { System.exit(0); return 1; } catch (Exception e) { return 2; } finally { System.out.println("finally..."); return 3; }}

System.exit(0)代表正常退出程序,直接結束,之后的代碼都不會運行。

6)

private static int i = 1;public static void main(String[] args) { int j = some(); System.out.println(j); System.out.println(i);}private static int some() { try { System.out.println(3 / 0); return 1; } catch (Exception e) { return i++; } finally { i++; }}

catch 子句中,此時 i = 1,將返回值 1 存入操作數棧,i++ 為 2;執行 finally 子句,i++ 為 3,再返回棧頂數 1。

輸出 13

7)

public static void main(String[] args) { System.out.println(some());}private static int some() { try { String[] strArr = new String[3]; if(strArr[2].equals("tom")){ System.out.println("try..."); } else { strArr[3] = "jerry"; } return 1; } catch (ArrayIndexOutOfBoundsException e) { return 2; } catch (NullPointerException e){ return 3; } finally { return 4; }}

strArr[2] 為 null,拋空指針異常,到對應 catch 塊,將返回值 3 存入操作數棧,執行 finally 子句,將返回值 4 存入操作數棧,返回棧頂數 4。

輸出 4

8)

public static void method1() { try { System.out.println("進入方法1"); throw new RuntimeException("異常1"); } finally { System.out.println("方法1的finally"); }}public static void method2() { try { System.out.println("進入方法2"); return; } finally { System.out.println("方法2的finally"); }}public static void main(String[] args) { try { method1(); } catch (Exception e) { System.out.println(e.getMessage()); } method2();}

調用 method1 方法,輸出 進入方法1,拋出異常,執行 finally 子句,輸出 方法1的finally,異常被 main 方法的 catch 子句捕捉,輸出 異常1

調用 method2 方法,輸出 進入方法2,return 前執行 finally 子句,輸出 方法2的finally

9)

public static void func() { try { throw new RuntimeException(); } finally { System.out.println("B"); }}public static void main(String[] args) { try { func(); System.out.println("A"); } catch (Exception e) { System.out.println("C"); } System.out.println("D");}

調用 func 方法,try 子句拋出異常,執行 finally 子句,輸出 B

拋出的異常被 main 方法的 catch 子句捕捉,輸出 C

輸出 D

d.2 鍵盤輸入得到整數后輸出到控制臺,若不是整數,循環輸入,直到得到整數為止。

錯誤示范:

Scanner scanner = new Scanner(System.in);while (true) { System.out.print("請輸入整數:"); // 判斷輸入字符是否是整數 if (scanner.hasNextInt()) { System.out.println(scanner.nextInt()); break; }}

如果字符緩沖區為空,會調用 read 方法等待鍵盤輸入,將輸入字符填充到字符緩沖區中,hasNextInt 方法讀取緩沖區中的內容,判斷下個 token 是否是整數;如果 token 不是整數,就會返回 false。

進入下一次循環,繼續調用 hasNextInt 方法,字符串緩沖區已有內容,不需鍵盤輸入,直接判斷 token 是否是整數,結果當然不是,然后無盡循環。

利用異常機制或判斷字符串是否能轉成整數的方法,當字符串不能轉為整數時就繼續循環。

Scanner scanner = new Scanner(System.in);while (true) { System.out.print("請輸入整數:"); String str = scanner.next(); try { System.out.println(Integer.parseInt(str)); break; } catch (NumberFormatException e) { }}public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (true) { System.out.print("請輸入整數:"); String str = scanner.next(); if (isNumber(str)) { System.out.println(Integer.parseInt(str)); break; } }}public static boolean isNumber(String str) { if (str == null || "".equals(str.trim())) { return false; } char[] chars = str.toCharArray(); for (int i = 0; i < chars.length; i++) { char aChar = chars[i]; if (aChar < '0' || aChar > '9') { return false; } } return true;}

拓展知識:

主站蜘蛛池模板: 熟妇五十路六十路息与子| 亚洲精品国产自在久久| 少妇又紧又色又爽又刺激视频 | 五月色丁香婷婷网蜜臀av| 久久九色综合九色99伊人| 欧美一亚洲一日韩一区二区三区| 免费99精品国产自在在线| 色噜噜狠狠色综合久夜色撩人| 亚洲精品第一区二区三区| 国产69精品久久久久人妻| 蜜臀在线一区二区| 少妇夜夜春夜夜爽试看视频| 久久久精品午夜免费不卡| 国内精品美女a∨在线播放| 天堂中文在线资源| 欧美日韩无线码在线观看| 护士张开腿被奷日出白浆| 日本一区二区偷拍综合怕图片| 99热亚洲色精品国产88| 一区二区三区久久99精品18禁| 国产又黄又粗又爽一区二区 | 国产现实无码av| 国内精品自在拍精选| 亚洲一区中文字幕在线| 日韩一区二区在线免费看av| 欧美牲交a欧美在线| 国产精品一区二区在线观看| 色婷婷孕妇av一区二区三区| 欧美内射深插日本少妇| 欧美一区二区三区日本一区二区三区| 小嫩妇好紧好爽再快视频| 2023极品少妇xxxo露脸| 妺妺窝人体色www看人体| 狠狠综合久久av一区二区| 国产精品口爆一区二区下载 | 成人免费无码av| 午夜人性色福利无码视频在线观看| 亚洲成av人最新无码| 国产精品天堂avav在线观看| 香蕉久久精品日日躁夜夜躁夏| 国内精品一区二区高跟鞋|