java 异常 日志_java中的异常、断言、日志(一)
1.Java異常處理i.異常的概念和Java里面的異常體系結構1)基本概念:程序中的異常,一般成為例外情況,可以理解為是非正常情況,其他編程語言里面也有這樣的情況,Java里面同樣存在這樣一個體系結構,這里需要分清楚的是異常和錯誤不是一個概念。異常并非是真正的錯誤,因為他們是一些例外情況,這些情況有可能不會導致系統直接崩潰掉,但是它的存在只能說是程序的某種缺陷,或者說是非必然缺陷,而Java里面提供的異常體系結構就是為了解決這些缺陷而存在的。在異常處理機制誕生之前,傳統的異常處理方式多數是為了采用返回值來標識程序出現異常的情況,這種方式都很熟悉,如同在調試過程即是有良好的調試工具,但是常用的手段就是System.out.println的方式,但是這樣的方式隱含一定的缺點。[1]一個API可以返回任意的值,但是這些返回值本身不能解釋返回值是否代表一個異常發生了,也不能描述異常的詳細情況,若要知道該API出現異常的一些內容,還需要調用它的某些方法;[2]沒有一種結構可以確保異常情況能夠得到處理,如果使用第一種方法,就會使得代碼的可讀性很差,而且很多時候并不能因為某些情況的存在就終止程序,就程序本身而言是應該提供一定的反饋情況。假設這樣一個場景,如果你去輸入用戶名和密碼登陸,如果你的用戶名和密碼輸入錯誤了,但是界面上沒有任何反應,這種情況是不是很糟糕,當然這只是個比方,這里不一定是出現了異常。在開發過程中,當一個程序本身拋出了異常過后,程序會從程序導致異常的地方跳出來,在java語言里面,使用try和catch塊來實現,當JVM碰到這個語句塊的時候,它會監聽整個Java程序,一旦出現了任何異常情況,它會將整個程序的執行權交給catch塊來執行。先看一段簡單的代碼:import java.io.File;import java.io.FileReader;/***一個簡單的文件操作**/public class CustomerFileReader{public static void main(String args[]){File file = new File("C:/read.txt");FileReader reader = new FileReader(file); //這句話不能通過JVM的編譯器}}上邊這段代碼如果使用的是javac的命令,那么編譯器就會報錯,可能錯誤信息如下:unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown那么如果要保證上邊這句代碼是能夠通過編譯的,如何簡單修改呢,有兩種方式:[1]加入try和catch塊,使用JVM的異常體系結構去捕捉該異常;[2]直接throws這個異常,讓程序顯式拋出該異常2)Java里面的異常體系結構:先看下邊的異常體系結構圖:Java里面的異常分類按照下邊的結構來分:Throwable是所有異常的基類,程序中一般不會直接拋出Throwable對象,Throwable本身存在兩個子類實例,一個是Error、一個是Exception;[1]Error:在Java里面Error表示程序在運行期間出現了十分嚴重的問題以及不可以恢復的錯誤,這種情況唯一的辦法是中止程序運行,JVM一般不會檢查Error是否被處理,而本身在程序中也不能捕獲Error類型的異常,因為Error一旦產生,該程序基本會處于需要中止的狀態。[2]Exception:在Java里面Exception指的就是在編程過程可能會遇到的異常的概念,也屬于Java程序里面允許出現的例外的狀況,而Exception本身分為以下兩種:RuntimeException:該異常繼承于Exception類,這種類型的異常可以這樣理解,為不可估計的異常,一般稱為運行時異常,從字面意思上講就是在程序正式運行的時候會碰到的偶發性異常,這種異常因為是在運行時拋出一般情況下不需要進行捕獲操作。CheckedException:該類異常不存在繼承于Exception類的說法,因為Java里面沒有CheckedException異常類,而之所以這樣區分,因為CheckedException類的異常是在編程過程中經常會遇到的異常,可以翻譯為“可檢測異常”或者“捕獲型異常”,該類異常往往屬于編譯期異常,一般開發過程會針對系統進行CheckedException的設計。【*:JVM在處理Java程序的時候,Runtime Exception和Checked Exception兩種類型的異常在運行機制上是不一樣的,而仔細考慮兩種異常的使用場合也會發現,其兩種異常在設計上所提及的使用目的也大相徑庭。從程序開發角度可以這樣理解:Checked Exception的設計目的在于這個Exception是必須被處理的,在程序設計過程應該知道這類異常會出現,所以要針對這一類型的異常進行不同的處理操作,這些異常也可以認為是在程序設計之初可以考慮到的異常;而RuntimeException可能理解起來隱晦一點,不能說不能考慮到這種異常的存在,反而是即使能夠考慮到,也不能進行良性的程序處理,它往往是暗示著程序可能會出現某種錯誤,這種錯誤有可能根程序本身無關,也有可能有關,是在設計程序之初是無法預知處理方式的,而有時候甚至會造成程序中止的情況。】這里提供一個簡單的關于RuntimeException的例子:public class RunExpTester{public static void main(String args[]){try{//……}catch(Exception ex){//……}}}考慮一下上邊這段代碼,上邊這段代碼使用了面對CheckedException的程序處理方式,使用了try和catch塊來處理有可能存在的Exception,但是這樣就出現了一個缺點:很可能不知道發生了什么異常,這些異常的源頭,而且如果整個程序段里面還包含了必須處理的CheckedException,那么這種設計方法反而給開發帶來了不必要的成本開銷。一般情況下,在測試階段,如果遇到了RuntimeException可以讓它這樣存在或者發生,然后再逐漸去修改的代碼,讓它盡量避免掉,否則面對任何一個Exception,都要確定不會輕易出現或者說更加完美一定不出現RuntimeException為之。這里提供一個常用的編程習慣作為參考,在使用Exception的catch過程的時候,一般可以這樣來書寫:catch(Exception ex){// TODO:書寫該異常的說明ex.printStackTrace();}當然這種情況是使用IDE的時候的一種習慣,一般IDE都提供了TODO:標記,使用該標記和所有TaskList的前綴標記不僅僅可以在開發過程了解到底有多少地方存在手寫的異常,而且在針對CheckedException的處理過程中,不是每一個異常都需要使用ex.printStackTrace()方法將該異常的堆棧信息全部打印出來,有時候需要在catch塊里面書寫更加實用的異常處理代碼。3)深入了解Throwable類:【參考API文檔】Throwable類是Java語言中所有錯誤(Error)或異常(Exception)的超類,只有當某個對象是該類的子類實例的時候,才能通過JVM或者Java本身編寫過程的throw語句拋出,按照這種邏輯區判斷,只有此類或者它的子類才可以是catch子句中的參數類型。Throwable類有兩個子類Error和Exception,上邊已經簡單介紹過這兩種類型的區別了。Throwable類本身包含:[1]線程創建的時候執行堆棧的快照[2]有關錯誤的消息字符串,比如該異常出現的位置以及代碼里面的哪一行[3]它指出了這個異常的原因:該異常是由哪個異常導致的或者說是由哪個異常拋出的Throwable導致的這個Throwable的產生從JDK 1.4開始,出現了一個異常處理的新概念:異常鏈(Cause機制)。異常鏈機制可以這樣理解:如果某個程序出現了異常,那么該異常本身也會有個原因,這個原因可能是自身的,也可能是外界的,以此類推就形成了一個異常鏈,簡單講:每個異常都是由另外一個異常引起的。而什么內容導致了throwable cause呢,查閱官方的API文檔有以下兩種解釋:[1]導致throwable cause的一個理由是,拋出它的類構建在低層抽象之中,而高層操作由于低層操作的失敗而失敗。讓低層拋出的throwable向外傳播是一種糟糕的設計方法,因為它通常與高層提供的抽象不相關。此外,這樣做將高層API與其實現細節關聯起來,假定低層異常是經過檢查的異常。拋出“經過包裝的異常”(即包含cause的異常)允許高層與其調用方交流失敗詳細信息,而不會招致上述任何一個缺點。這種方式保留了改變高層實現而不改變其 API 的靈活性[2]導致throwable cause的另一個cause是,拋出它的方法必須符合通用接口,而通用接口不允許方法直接拋出cause。例如,假定持久集合符合Collection接口,而其持久性在java.io的基礎上實現。假定add方法的內部可以拋出IOException。實現可以與其調用方交流IOException的詳細消息,同時通過以一種合適的未檢查的異常來包裝IOException,使其符合Collection接口。4)特殊類AssertionError:這里提供一段代碼:public class AssertionErrorTester{public static void main(String args[]){try{assert args.length < 0:"Args Length Error!";}catch(AssertionError e){String message = e.getMessage();System.out.println("Error Source:" + message);}}}【*:這里需要提及的一個異常類是AssertionError類,因為上邊已經講過了,所有的Error都是不能進行catch的處理的,但是AssertionError屬于一個比較特殊的類,因為JVM針對AssertionError類是可以進行catch處理的,該Error和普通的Error可能存在本質的區別】使用斷言編譯方式編譯以上代碼,然后打開斷言執行該編譯好的class文件,會出現以下輸出:Error Source:Args Length Error!關于如何使用斷言編譯以及斷言本身的使用規則在斷言章節會涉及到,這里先不做詳細講解ii.異常的基本語法前邊介紹了Java異常體系結構、分類以及基本概念,這一小節需要介紹的就是Java里面異常的基本語法。在Java里面,異常處理機制的編程部分需要使用到幾個關鍵字:try、catch、finally、throw、throws1)try、catch、finally關鍵字:/***測試Exception關鍵字的代碼**/public class ExceptionTester{public static void main(String args[]){//這一塊被成為異常的正常執行塊,也就是如果沒有異常拋出的話,try塊會一直這樣正常執行到最末位try{//判斷輸入參數的長度if( args.length == 0 ){System.out.println("Args length is zore");}else{String inputString = args[0];int inputNumber = Integer.parseInteger(inputString);System.out.println("Input number is " + inputNumber);}}//這一塊是異常處理塊,一旦當try代碼塊里面拋出了異常的時候,直接從try塊中斷,直接進行catch代碼塊的執行catch(Exception ex){ex.printStackTrace();}//finally代碼塊,不論try中是否拋出異常,也不論catch里面是否真正能夠捕捉到異常,finally里面的代碼都會執行(有例外)finally{System.out.println("Testing finishing...");}}}這段代碼出現了三個關鍵字,try、catch、finally,這里先對這三個關鍵字簡單講解:try語句:該語句塊屬于代碼的正常執行塊,如果這段代碼里面不會出現任何異常,那么try語句塊會按照順序一直執行下去catch語句:該語句塊的參數類似于平時在代碼書寫中的方法聲明,包含了一個異常類型以及一個異常對象。這里結合第一節講到的,異常類型必須是Throwable的子類型,它指出了catch語句處理的異常類型,異常對象則有可能在try語句塊里面生成,而且被對應的catch塊里面的異常類型所捕獲,大括號里面的內容就是當你捕獲了對應的異常過后如何進行處理。在異常處理里面,catch語句塊可以有多個,分別處理不同的異常。Java運行的時候一旦拋出了異常就從catch塊從上往下檢索,一旦匹配對應的類型就執行catch塊里面的內容,所以這里有一點需要注意:catch塊里面的異常類型的順序,一般情況是從特殊到一般,然后是從子類到父類,否則會造成代碼不可達的無用代碼塊finally語句:該語句塊可以指定一個段代碼塊,不論try塊也好、catch塊也好,也不論異常是否拋出,最終都會執行finally塊里面的內容,可以這樣理解:finally塊里面是異常處理機制的統一出口,只要存在這樣的一段代碼塊最終出口都是執行完finally塊里面的內容了再繼續。【*:但是有一個特殊的情況,如果try塊里面出現了return語句,那么finally塊里面的內容是不會執行的,但是這種做法不提倡。】2)throw、throws關鍵字:import java.io.File;import java.io.FileReader;import java.io.IOException;/***提供throw和throws關鍵字的代碼塊**/public class ThrowInstance{public static void main(String args[]) throws Exception{try{readFile("C:/read.txt");}catch(IOException ex){throw new Exception(ex);}}public static void readFile(String path) throws IOException{File file = new File(path);FileReader reader = new FileReader(file);}}從上邊這段代碼理解throw和throws關鍵字:throw關鍵字:throw關鍵字總是出現在函數體內部,用來拋出一個異常,程序會在throw語句后立即終止執行,也就是說位于throw語句之后的語句塊不會執行,一旦它拋出了一個異常過后,JVM會在包含它的try塊所對應的catch里面根據拋出的異常類型匹配操作,如果能匹配就直接被捕捉,一旦不能匹配就繼續往執行體的外層拋出該異常。throws關鍵字:throws關鍵字總是出現在函數頭部,用來表明該函數有可能會拋出某種異常,有幾點需要注意:[1]函數可以拋出多個不同類型的異常,直接使用,將每種拋出的不同異常分開;[2]如果函數體里面存在throw語句,而且函數體本身沒有進行捕捉的話,那么必須使用throws在函數頭里面添加對應的異常拋出語句,否則無法通過編譯[3]如果編寫代碼的時候需要明確拋出一個RuntimeException,那么必須顯示使用throws語句來聲明它的類型[4]以上的規則主要是針對CheckedException,針對Error和RuntimeException或者它們的子類,這些規則不起作用3)關鍵字的搭配:try+catch:這是常用的代碼結構,這種情況類似下邊這種情況:/***try+catch語句塊**/public class TryCatch{public static void main(String args[]){try{//正常執行語句塊}catch(Exception ex){//拋出異常過后的異常捕捉語句塊,捕捉到異常了就執行}}}這種語句塊的執行流程為:運行try塊中的代碼,如果有異常拋出,就會轉到catch語句塊中執行,當然前提是catch中的異常類型和try塊中拋出的異常類型匹配。try+catch+finally/***try+catch+finally語句塊**/public class TryCatch{public static void main(String args[]){try{//正常執行語句塊
總結
以上是生活随笔為你收集整理的java 异常 日志_java中的异常、断言、日志(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 获取域名部分,PHP 获取顶级域
- 下一篇: linux中如何在文件中查找文件,lin