日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java异常处理学习总结

發(fā)布時間:2025/4/14 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java异常处理学习总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Java異常的棧軌跡(Stack Trace)

? ? 捕獲到異常時,往往需要進(jìn)行一些處理。比較簡單直接的方式就是打印異常棧軌跡Stack Trace。說

起棧軌跡,可能很多人和我一樣,第一反應(yīng)就是printStackTrace()方法。其實除了這個方法,還有一些

別的內(nèi)容也是和棧軌跡有關(guān)的。

? ? ?1.printStackTrace()

? ? 首先需要明確,這個方法并不是來自于Exception類。Exception類本身除了定義了幾個構(gòu)造器之外

,所有的方法都是從其父類繼承過來的。而和異常相關(guān)的方法都是從java.lang.Throwable類繼承過來的

。而printStackTrace()就是其中一個。

? ? ?這個方法會將Throwable對象的棧軌跡信息打印到標(biāo)準(zhǔn)錯誤輸出流上。輸出的大體樣子如下:


java.lang.NullPointerException
? ? ? ? ?at MyClass.mash(MyClass.java:9)
? ? ? ? ?at MyClass.crunch(MyClass.java:6)
? ? ? ? ?at MyClass.main(MyClass.java:3)
? ?輸出的第一行是toString()方法的輸出,后面幾行的內(nèi)容都是之前通過fillInStackTrace()方法保存

的內(nèi)容。關(guān)于這個方法,我們后面會講。

? ? 下面看一個例子:

public class TestPrintStackTrace {
? ? public static void f() throws Exception{
? ? ? ? throw new Exception("出問題啦!");
? ? }
? ? public static void g() throws Exception{
? ? ? ? f();
? ? }
? ? public static void main(String[] args) {
? ? ? ? try {
? ? ? ? ? ? g();
? ? ? ? }catch(Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
? ? 這個例子的輸出如下:


java.lang.Exception: 出問題啦!
? ? at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
? ? at TestPrintStackTrace.g(TestPrintStackTrace.java:6)
? ? at TestPrintStackTrace.main(TestPrintStackTrace.java:10)
? ? 在這個例子中,在方法f()中拋出異常,方法g()中調(diào)用方法f(),在main方法中捕獲異常,并且打印


棧軌跡信息。因此,輸出依次展示了f—>g—>main的過程。

? ? 2.getStackTrace()方法

? ? 這個方法提供了對printStackTrace()方法所打印信息的編程訪問。它會返回一個棧軌跡元素的數(shù)組

。以上面的輸出為例,輸出的第2-4行每一行的內(nèi)容對應(yīng)一個棧軌跡元素。將這些棧軌跡元素保存在一個

數(shù)組中。每個元素對應(yīng)棧的一個棧幀。數(shù)組的第一個元素保存的是棧頂元素,也就是上面的f。最后一個

元素保存的棧底元素。

? ? 下面是一個使用getStackTrace()訪問這些軌跡棧元素并打印輸出的例子:


public class TestPrintStackTrace {
? ? public static void f() throws Exception{
? ? ? ? throw new Exception("出問題啦!");
? ? }
? ? public static void g() throws Exception{
? ? ? ? f();
? ? }
? ? public static void main(String[] args) {
? ? ? ? try {
? ? ? ? ? ? g();
? ? ? ? }catch(Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? System.out.println("------------------------------");
? ? ? ? ? ? for(StackTraceElement elem : e.getStackTrace()) {
? ? ? ? ? ? ? ? System.out.println(elem);
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
? ? 這樣的輸出和printStackTrace()的輸出基本上是一樣的,如下:


java.lang.Exception: 出問題啦!
? ? at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
? ? at TestPrintStackTrace.g(TestPrintStackTrace.java:6)
? ? at TestPrintStackTrace.main(TestPrintStackTrace.java:10)
TestPrintStackTrace.f(TestPrintStackTrace.java:3)
TestPrintStackTrace.g(TestPrintStackTrace.java:6)
TestPrintStackTrace.main(TestPrintStackTrace.java:10)
? ?3.fillInStackTrace()


? ? 我們在前面也提到了這個方法。要說清楚這個方法,首先要講一下捕獲異常之后重新拋出的問題。

在catch代碼塊中捕獲到異常,打印棧軌跡,又重新throw出去。在上一級的方法調(diào)用中,再捕獲這個異

常并且打印出棧軌跡信息。這兩個棧軌跡信息會一樣嗎?我們看一下代碼:

public class TestPrintStackTrace {
? ? public static void f() throws Exception{
? ? ? ? throw new Exception("出問題啦!");
? ? }
? ? public static void g() throws Exception{
? ? ? ? try {
? ? ? ? ? ? f();
? ? ? ? }catch(Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? throw e;
? ? ? ? }
? ? ? ? ?
? ? }
? ? public static void main(String[] args) {
? ? ? ? try {
? ? ? ? ? ? g();
? ? ? ? }catch(Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
? ? 在main方法中捕獲的異常,是在g()方法中拋出的,按理說這兩個打印棧軌跡的信息應(yīng)該不同,第二


次打印的信息應(yīng)該沒有關(guān)于f的信息。但是事實上,兩次打印棧軌跡信息是一樣的。輸出結(jié)果如下:


java.lang.Exception: 出問題啦!
? ? at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
? ? at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
? ? at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
java.lang.Exception: 出問題啦!
? ? at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
? ? at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
? ? at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
? ? 也就是說,捕獲到異常又立即拋出,在上級方法調(diào)用中再次捕獲這個異常,打印的棧軌跡信息是一


樣的。原因在于沒有將當(dāng)前線程當(dāng)前狀態(tài)下的軌跡棧的狀態(tài)保存進(jìn)Throwabe中。現(xiàn)在我們引入


fillInStackTrace()方法。這個方法剛好做的就是這樣的保存工作。我們看一下這個方法的原型:


1
public Throwable fillInStackTrace()
? ? 這個方法是有返回值的。返回的是保存了當(dāng)前棧軌跡信息的Throwable對象。我們看看使用


fillInStackTrace()方法處理后,打印的棧軌跡信息有什么不同,代碼如下:


public class TestPrintStackTrace {
? ? public static void f() throws Exception{
? ? ? ? throw new Exception("出問題啦!");
? ? }
? ? public static void g() throws Exception{
? ? ? ? try {
? ? ? ? ? ? f();
? ? ? ? }catch(Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? //不要忘了強(qiáng)制類型轉(zhuǎn)換
? ? ? ? ? ? throw (Exception)e.fillInStackTrace();
? ? ? ? }
? ? ? ? ?
? ? }
? ? public static void main(String[] args) {
? ? ? ? try {
? ? ? ? ? ? g();
? ? ? ? }catch(Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
}
? ? ?輸出如下:


java.lang.Exception: 出問題啦!
? ? at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
? ? at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
? ? at TestPrintStackTrace.main(TestPrintStackTrace.java:17)
java.lang.Exception: 出問題啦!
? ? at TestPrintStackTrace.g(TestPrintStackTrace.java:11)
? ? at TestPrintStackTrace.main(TestPrintStackTrace.java:17)
? ? 我們看到,在main方法中打印棧軌跡已經(jīng)沒有了f相關(guān)的信息了。


? ? 以上就是關(guān)于Java棧軌跡的一些我之前沒有掌握的內(nèi)容,記下來備忘。?
========

java web項目整體異常處理機(jī)制


在實際的j2ee項目中,系統(tǒng)內(nèi)部難免會出現(xiàn)一些異常,如果把異常放任不管直接打印到瀏覽器可能會讓

用戶感覺莫名其妙,也有可能讓某些用戶找到破解系統(tǒng)的方法。
出來工作一年時間了,我也大概對異常處理有了一些了解,在這呢小弟簡單介紹下個人對異常處理的見

解,拋磚引玉,希望各位大神提出寶貴的意見和建議。
?
就拿spring+struts2+hibernate項目說明:通常一個頁面請求到后臺以后,首先是到action(也就是所

謂mvc的controller),在action層會調(diào)用業(yè)務(wù)邏輯service,servce層會調(diào)用持久層dao獲取數(shù)據(jù)。最后

執(zhí)行結(jié)果會匯總到action,然后通過action控制轉(zhuǎn)發(fā)到指定頁面,執(zhí)行流程如下圖所示:

?
而這三層其實都有可能發(fā)生異常,比如dao層可能會有SQLException,service可能會有

NullPointException,action可能會有IOException,一但發(fā)生異常并且程序員未做處理,那么該層不會

再往下執(zhí)行,而是向調(diào)用自己的方法拋出異常,如果dao、service、action層都未處理異常的話,異常

信息會拋到服務(wù)器,然后服務(wù)器會把異常直接打印到頁面,結(jié)果就會如下圖所示:

?
其實這種錯誤對于客戶來說毫無意義,因為他們通常是看不懂這是什么意思的。
剛學(xué)java的時候,我們處理異常通常兩種方法:①直接throws,放任不管;②寫try...catch,在catch

塊中不作任何操作,或者僅僅printStackTrace()把異常打印到控制臺。第一種方法最后就造就了上圖的

結(jié)果;而第二種方法更杯具:頁面不報錯,但是也不執(zhí)行用戶的請求,簡單的說,其實這就是bug(委婉

點:通常是這樣)!
?
那么發(fā)生異常到底應(yīng)該怎么辦呢?我想在大家對java異常有一定了解以后,會知道:異常應(yīng)該在action

控制轉(zhuǎn)發(fā)之前盡量處理,同時記錄log日志,然后在頁面以友好的錯誤提示告訴用戶出錯了。大家看下面

的代碼:
Java代碼 ?收藏代碼
//創(chuàng)建日志對象 ?
Log log = LogFactory.getLog(this.getClass()); ?
??
//action層執(zhí)行數(shù)據(jù)添加操作 ?
public String save(){ ?
? ?try{ ?
? ? ? ? ?//調(diào)用service的save方法 ?
? ? ? ? ?service.save(obj); ?
? ?}catch(Exception e){ ?
? ? ? ? ?log.error(...); ? //記錄log日志 ?
? ? ? return "error"; 到指定error頁面 ?
? ?} ?
? ?return "success"; ?
} ?
?
如果按照上面的方式處理異常以后,我們用戶最后看到的頁面可能就會是下面這種形式(我想這種錯誤


提示應(yīng)該稍微友好點了吧):


?
然后我們回到剛才處理異常的地方,如果大家積累了一些項目經(jīng)驗以后會發(fā)現(xiàn)使用上面那種處理異常的

方式可能還不夠靈活:
①因為spring把大多數(shù)非運(yùn)行時異常都轉(zhuǎn)換成運(yùn)行時異常(RuntimeException)最后導(dǎo)致程序員根本不

知道什么地方應(yīng)該進(jìn)行try...catch操作
②每個方法都重復(fù)寫try...catch,而且catch塊內(nèi)的代碼都很相似,這明顯做了很多重復(fù)工作而且還很

容易出錯,同時也加大了單元測試的用例數(shù)(項目經(jīng)理通常喜歡根據(jù)代碼行來估算UT case)
③發(fā)生異常有很多種情況:可能有數(shù)據(jù)庫增刪改查錯誤,可能是文件讀寫錯誤,等等。用戶覺得每次發(fā)

生異常都是“訪問過程中產(chǎn)生錯誤,請重試”的提示完全不能說明錯誤情況,他們希望讓異常信息更詳

盡些,比如:在執(zhí)行數(shù)據(jù)刪除時發(fā)生錯誤,這樣他們可以更準(zhǔn)確地給維護(hù)人員提供bug信息。
?
如何解決上面的問題呢?我是這樣做的:JDK異常或自定義異常+異常攔截器
struts2攔截器的作用在網(wǎng)上有很多資料,在此不再贅述,我的異常攔截器原理如下圖所示:

?首先我的action類、service類和dao類如果有必要捕獲異常,我都會try...catch,catch塊內(nèi)不記錄

log,通常是拋出一個新異常,并且注明錯誤信息:
Java代碼 ?收藏代碼
//action層執(zhí)行數(shù)據(jù)添加操作 ?
public String save(){ ?
? ?try{ ?
? ? ? ? ?//調(diào)用service的save方法 ?
? ? ? ? ?service.save(obj); ?
? ?}catch(Exception e){ ?
? ? ? //你問我為什么拋出Runtime異常?因為我懶得在方法后寫throws ?xx ?
? ? ? throw new RuntimeException("添加數(shù)據(jù)時發(fā)生錯誤!",e); ?
? } ?
? ?return "success"; ?
} ?
??
然后在異常攔截器對異常進(jìn)行處理,看下面的代碼:
Java代碼 ?收藏代碼
public String intercept(ActionInvocation actioninvocation) { ?
??
? ? ? ? String result = null; // Action的返回值 ?
? ? ? ? try { ?
? ? ? ? ? ? // 運(yùn)行被攔截的Action,期間如果發(fā)生異常會被catch住 ?
? ? ? ? ? ? result = actioninvocation.invoke(); ?
? ? ? ? ? ? return result; ?
? ? ? ? } catch (Exception e) { ?
? ? ? ? ? ? /**?
? ? ? ? ? ? ?* 處理異常?
? ? ? ? ? ? ?*/ ?
? ? ? ? ? ? String errorMsg = "未知錯誤!"; ?
? ? ? ? ? ? //通過instanceof判斷到底是什么異常類型 ?
? ? ? ? ? ? if (e instanceof BaseException) { ?
? ? ? ? ? ? ? ? BaseException be = (BaseException) e; ?
? ? ? ? ? ? ? ? be.printStackTrace(); //開發(fā)時打印異常信息,方便調(diào)試 ?
? ? ? ? ? ? ? ? if(be.getMessage()!=null||Constants.BLANK.equals(be.getMessage().trim())){ ?
? ? ? ? ? ? ? ? ? ? //獲得錯誤信息 ?
? ? ? ? ? ? ? ? ? ? errorMsg = be.getMessage().trim(); ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? } else if(e instanceof RuntimeException){ ?
? ? ? ? ? ? ? ? //未知的運(yùn)行時異常 ?
? ? ? ? ? ? ? ? RuntimeException re = (RuntimeException)e; ?
? ? ? ? ? ? ? ? re.printStackTrace(); ?
? ? ? ? ? ? } else{ ?
? ? ? ? ? ? ? ? //未知的嚴(yán)重異常 ?
? ? ? ? ? ? ? ? e.printStackTrace(); ?
? ? ? ? ? ? } ?
? ? ? ? ? ? //把自定義錯誤信息 ?
? ? ? ? ? ? HttpServletRequest request = (HttpServletRequest) actioninvocation ?
? ? ? ? ? ? ? ? ? ? .getInvocationContext().get(StrutsStatics.HTTP_REQUEST); ?
? ? ? ? ? ? ??
? ? ? ? ? ? /**?
? ? ? ? ? ? ?* 發(fā)送錯誤消息到頁面?
? ? ? ? ? ? ?*/ ?
? ? ? ? ? ? request.setAttribute("errorMsg", errorMsg); ?
? ? ? ? ??
? ? ? ? ? ? /**?
? ? ? ? ? ? ?* log4j記錄日志?
? ? ? ? ? ? ?*/ ?
? ? ? ? ? ? Log log = LogFactory ?
? ? ? ? ? ? ? ? ? ? .getLog(actioninvocation.getAction().getClass()); ?
? ? ? ? ? ? if (e.getCause() != null){ ?
? ? ? ? ? ? ? ? log.error(errorMsg, e); ?
? ? ? ? ? ? }else{ ?
? ? ? ? ? ? ? ? log.error(errorMsg, e); ?
? ? ? ? ? ? } ?
??
? ? ? ? ? ? return "error"; ?
? ? ? ? }// ...end of catch ?
? ? } ?
?需要注意的是:在使用instanceof判斷異常類型的時候一定要從子到父依次找,比如BaseException繼承

與RuntimeException,則必須首先判斷是否是BaseException再判斷是否是RuntimeException。
??
最后在error JSP頁面顯示具體的錯誤消息即可:
Java代碼 ?收藏代碼
<body> ?
<s:if test="%{#request.errorMsg==null}"> ?
? ? <p>對不起,系統(tǒng)發(fā)生了未知的錯誤</p> ?
</s:if> ?
<s:else> ?
? ? <p>${requestScope.errorMsg}</p> ?
</s:else> ?
</body> ?
?
以上方式可以攔截后臺代碼所有的異常,但如果出現(xiàn)數(shù)據(jù)庫連接異常時不能被捕獲的,大家可以使用

struts2的全局異常處理機(jī)制來處理:
Java代碼 ?收藏代碼
<global-results> ?
? ? <result name="error" >/Web/common/page/error.jsp</result> ?
</global-results> ?
? ? ? ? ? ?
<global-exception-mappings> ?
? ? <exception-mapping result="error" exception="java.lang.Exception"></exception-mapping> ?
</global-exception-mappings> ?
?
上面這是一個很簡單的異常攔截器,大家可以使用自定義異常,那樣會更靈活一些。
?
以上異常攔截器可以使用其它很多技術(shù)替換:比如spring aop,servlet filter等,根據(jù)項目實際情況

處理。
?
【補(bǔ)充】ajax也可以進(jìn)行攔截,但是因為ajax屬于異步操作,action通過response形式直接把數(shù)據(jù)返回

給ajax回調(diào)函數(shù),如果發(fā)生異常,ajax是不會執(zhí)行頁面跳轉(zhuǎn)的,所以必須把錯誤信息返回給回調(diào)函數(shù),

我針對json數(shù)據(jù)的ajax是這樣做的:
Java代碼 ?收藏代碼
/**?
?* 讀取文件,獲取對應(yīng)錯誤消息?
?*/ ?
HttpServletResponse response = (HttpServletResponse)actioninvocation.getInvocationContext

().get(StrutsStatics.HTTP_RESPONSE); ?
response.setCharacterEncoding(Constants.ENCODING_UTF8); ?
/**?
?* 發(fā)送錯誤消息到頁面?
?*/ ?
PrintWriter out; ?
try { ?
? ? out = response.getWriter(); ?
? ? Message msg = new Message(errorMsg); ?
? ? //把異常信息轉(zhuǎn)換成json格式返回給前臺 ?
? ? out.print(JSONObject.fromObject(msg).toString()); ?
} catch (IOException e1) { ?
? ? throw e; ?
} ?
?
========

總結(jié)

以上是生活随笔為你收集整理的java异常处理学习总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。