異常是Java程序中經(jīng)常遇到的問題,一個異常就是一個Bug,就要花很多時間來定位異常。
Java異常
(1)Throwable是Java異常的頂級類,所有的異常都繼承于這個類。
(2)Error,Exception是異常類的兩個大分類。
(3)Error是非程序異常,即程序不能捕獲的異常,一般是編譯或者系統(tǒng)性的錯誤,如OutOfMemorry內(nèi)存溢出異常等。
(4)Exception是程序異常類,由程序內(nèi)部產(chǎn)生。Exception又分為運行時異常、非運行時異常。
(5)運行時異常的特點是Java編譯器不會檢查它,也就是說,當程序中可能出現(xiàn)這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句聲明拋出它,也會編譯通過,運行時異常可處理或者不處理。運行時異常一般常出來定義系統(tǒng)的自定義異常,業(yè)務根據(jù)自定義異常做出不同的處理。常見的運行時異常如NullPointException、ArrayIndexOutOfBoundsException等。
(6)非運行時異常是程序必須進行處理的異常,捕獲或者拋出,如果不處理程序就不能編譯通過。如常見的IOException、ClassNotFoundException等。
常見的Java異常坑
最頑固的坑--NullPointerException
空指針異常應該是每一個程序員必須要踩得的坑,而且應該是經(jīng)常踩不斷踩的坑,極其普遍且難以根除。但是這個坑可以通過某些方法有效的避免。
(1)什么是空指針?
當一個變量的值為 null 時,在 Java 里面表示一個不存在的空對象,沒有實際內(nèi)容,沒有給它分配內(nèi)存,null 也是對象成員變量的默認值。
所以,一個對象如果沒有進行初始化操作,這時候,如果你調(diào)用這個對象的方法或者變量,就會出現(xiàn)空指針異常。
如下面示例會發(fā)生空指針異常:
Object?object?=?null;
String?string?=?object.toString();
空指針它是屬于運行時異常?RuntimeException?的子類,它不是捕獲型的,只有在程序運行時才可能報出來,而且會造成程序中斷。
如何有效避免空指針?
以下是我在編碼過程中遇到的問題及解決辦法。
字符串比較,常量放前面---總是從已知的非空String對象中調(diào)用equals()方法
if(status.equals(SUCCESS)){
}
這個時候 status 可能為 null 造成空指針異常,應該把常量放前面,就能避免空指針異常。這個應該在各種開發(fā)規(guī)范里面都會提到,也是最基礎(chǔ)的。
if(SUCCESS.equals(status)){
}
當valueOf()和toString()返回相同的結(jié)果時,寧愿使用前者。
因為調(diào)用null對象的toString()會拋出空指針異常,如果我們能夠使用valueOf()獲得相同的值,那寧愿使用valueOf(),傳遞一個null給valueOf()將會返回“null”,尤其是在那些包裝類,像Integer、Float、Double和BigDecimal。
BigDecimal bd = getPrice();
System.out.println(String.valueOf(bd));?//不會拋出空指針異常
System.out.println(bd.toString());?//拋出 NullPointerException
初始化默認值
在對象初始化的時候給它一個默認值或者默認構(gòu)造實現(xiàn),如:
User?user?=?new?User();
String?name?=?StringUtils.EMPTY;
避免返回空集合
在返回一個集合的話,默認會是 null,統(tǒng)一規(guī)范返回一個空集合。
舉個 List 例子,如:
public?List?getUserList(){
????List?list?=?userMapper.gerUserList();
????return?list?==?null???new?ArrayList()?:?list;
}
這樣接收方就不用擔心空指針異常了,也不會影響業(yè)務。
采用JDK8 Optional?新特性
?Optional是Jdk8提供的一個可以包含null值的容器對象,可以用來代替xx != null的判斷。(這個暫時因為我們車道系統(tǒng)統(tǒng)一適用的是Java7,這個方案還沒有在車到系統(tǒng)代碼里使用過。)
OutOfMemoryError
內(nèi)存異常異常也是一個經(jīng)常出現(xiàn)的Bug,但是這個不是程序能控制的,這個問題是指要分配的對象的內(nèi)存超出了當前大的堆內(nèi)存,需要調(diào)整堆內(nèi)存大小(-Xmx)以及優(yōu)化程序。
(1)常見的有以下幾種原因:
1.內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);
2.集合類中有對對象的引用使用完后未清空,使得JVM不能回收;
3.代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復的對象實體;
4.使用的第三方軟件中的BUG;
5.啟動參數(shù)內(nèi)存值設(shè)定的過小
(2)常見解決方法:
1.應用服務器提示錯誤的解決:
??把啟動參數(shù)內(nèi)存值設(shè)置足夠大。
2.Java代碼導致錯誤的解決:
1)檢查代碼中是否有死循環(huán)或遞歸調(diào)用。
2)檢查是否有大循環(huán)重復產(chǎn)生新對象實體。
3)檢查對數(shù)據(jù)庫查詢中,是否有一次獲得全部數(shù)據(jù)的查詢。一般來說,如果一次取十萬條記錄到內(nèi)存,就可能引起內(nèi)存溢出。這個問題比較隱蔽,在上線前,數(shù)據(jù)庫中數(shù)據(jù)較少,不容易出問題,上線后,數(shù)據(jù)庫中數(shù)據(jù)多了,一次查詢就有可能引起內(nèi)存溢出。因此對于數(shù)據(jù)庫查詢盡量采用分頁的方式查詢。
4 )檢查List、MAP等集合對象是否有使用完后,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。
IOException
IO,即:input, output,我們在讀寫磁盤文件、網(wǎng)絡(luò)內(nèi)容的時候經(jīng)常會生的一種異常,這種異常是受檢查異常,需要進行手工捕獲。常用的一種異常處理方式有兩種,一種是:使用throws拋出可能發(fā)生的異常,另一種是:直接try-catch。
ClassNotFoundException
類找不到異常,Java開發(fā)中經(jīng)常遇到,是不是很絕望?這是在加載類的時候拋出來的,即在類路徑下不能加載指定的類。
注意:在車道程序中不推薦使用throws把異常拋給系統(tǒng)處理,車道系統(tǒng)要捕獲所有能捕獲的異常并進行處理,目的是當發(fā)生程序級異常時要保證車道程序可以正常收費,不可因為程序異常影響到正在進行的收費處理。
異常處理的一般原則
1、 能處理就早處理,拋出不去還不能處理的異常就要想辦法消化掉,或者轉(zhuǎn)換為RuntimeException處理。因為對于一個應用系統(tǒng)來說,拋出大量異常是有問題的,應該從程序開發(fā)角度盡可能的控制異常發(fā)生的可能。
2、 對于檢查異常,如果不能行之有效的處理,還不如轉(zhuǎn)換為RuntimeException拋出。這樣也讓上層的代碼有選擇的余地――可處理也可不處理。
3、 對于一個應用系統(tǒng)來說,應該有自己的一套異常處理框架,這樣當異常發(fā)生時,也能得到統(tǒng)一的處理風格,將優(yōu)雅的異常信息反饋給用戶。
異常的轉(zhuǎn)譯與異常鏈?
?1、異常轉(zhuǎn)譯的原理?
所謂的異常轉(zhuǎn)譯就是將一種異常轉(zhuǎn)換另一種新的異常,也許這種新的異常更能準確表達程序發(fā)生異常。
在Java中有個概念就是異常原因,異常原因?qū)е庐斍皰伋霎惓5哪莻€異常對象,幾乎所有帶異常原因的異常構(gòu)造方法都使用Throwable類型做參數(shù),這也就為異常的轉(zhuǎn)譯提供了直接的支持,因為任何形式的異常和錯誤都是Throwable的子類。比如將SQLException轉(zhuǎn)換為另外一個新的異常DAOException,可以這么寫:
??? 先自定義一個異常DAOException:
???比如有一個SQLException類型的異常對象e,要轉(zhuǎn)換為DAOException,可以這么寫:??
public class DAOException extends RuntimeException {
//(省略了部分代碼)
public DAOException(String message, Throwable cause) {
super(message, cause);
}
}
DAOException daoEx = new DAOException ( "SQL異常", e);
異常轉(zhuǎn)譯是針對所有繼承Throwable超類的類而言的,從編程的語法角度講,其子類之間都可以相互轉(zhuǎn)換。但是,從合理性和系統(tǒng)設(shè)計角度考慮,可將異常分為三類:Error、Exception、RuntimeException。
?異常的處理存在著一套哲學思想:對于一個應用系統(tǒng)來說,系統(tǒng)所發(fā)生的任何異常或者錯誤對操作用戶來說都是系統(tǒng)"運行時"異常,都是這個應用系統(tǒng)內(nèi)部的異常。這也是異常轉(zhuǎn)譯和應用系統(tǒng)異常框架設(shè)計的指導原則。在系統(tǒng)中大量處理非檢查異常的AxiTrader返傭www.fx61.com/brokerlist/axitrader.html負面影響很多,最重要的一個方面就是代碼可讀性降低,程序編寫復雜,異常處理的代碼也很蒼白無力。因此,很有必要將這些檢查異常Exception和錯誤Error轉(zhuǎn)換為RuntimeException異常,讓程序員根據(jù)情況來決定是否捕獲和處理所發(fā)生的異常。
①:Error到Exception:將錯誤轉(zhuǎn)換為異常,并繼續(xù)拋出。例如Spring WEB框架中,將org.springframework.web.servlet.DispatcherServlet的doDispatch()方法中,將捕獲的錯誤轉(zhuǎn)譯為一個NestedServletException異常。這樣做的目的是為了大限度挽回因錯誤發(fā)生帶來的負面影響。因為一個Error常常是很嚴重的錯誤,可能會引起系統(tǒng)掛起。
②:Exception到RuntimeException:將檢查異常轉(zhuǎn)換為RuntimeException可以讓程序代碼變得更優(yōu)雅,讓開發(fā)人員集中精力設(shè)計更合理的程序代碼,反過來也增加了系統(tǒng)發(fā)生異常的可能性。
③:Error到RuntimeException:目的還是一樣的。把所有的異常和錯誤轉(zhuǎn)譯為不檢查異常,這樣可以讓代碼更為簡潔,還有利于對錯誤和異常信息的統(tǒng)一處理。
1、 異常鏈
異常鏈顧名思義就是將異常發(fā)生的原因一個傳一個串起來,即把底層的異常信息傳給上層,這樣逐層拋出。Java API文檔中給出了一個簡單的模型:?
try {
lowLevelOp();
} catch (LowLevelException le) {
throw (HighLevelException)
new HighLevelException().initCause(le);
}
當程序捕獲到了一個底層異常le,在處理部分選擇了繼續(xù)拋出一個更高級別的新異常給此方法的調(diào)用者。這樣異常的原因就會逐層傳遞。這樣,位于高層的異常遞歸調(diào)用getCause()方法,就可以遍歷各層的異常原因。這就是Java異常鏈的原理。異常鏈的實際應用很少,發(fā)生異常時候逐層上拋不是個好注意,上層拿到這些異常又能奈之何?而且異常逐層上拋會消耗大量資源,因為要保存一個完整的異常鏈信息。
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
分享文章:Java常見異常處理-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://vcdvsql.cn/article40/hoceo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導航、ChatGPT、網(wǎng)站制作、小程序開發(fā)、定制開發(fā)、網(wǎng)站營銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容