Java 異常的棧軌跡(Stack Trace)詳解及實例代碼
Java 異常的棧軌跡(Stack Trace)詳解
捕獲到異常時,往往需要進行一些處理。比較簡單直接的方式就是打印異常棧軌跡Stack Trace。說起棧軌跡,可能很多人和我一樣,第一反應就是printStackTrace()方法。其實除了這個方法,還有一些別的內容也是和棧軌跡有關的。
1.printStackTrace()
首先需要明確,這個方法并不是來自于Exception類。Exception類本身除了定義了幾個構造器之外,所有的方法都是從其父類繼承過來的。而和異常相關的方法都是從java.lang.Throwable類繼承過來的。而printStackTrace()就是其中一個。
這個方法會將Throwable對象的棧軌跡信息打印到標準錯誤輸出流上。輸出的大體樣子如下:
java.lang.NullPointerException
at MyClass.mash(MyClass.java:9)
at MyClass.crunch(MyClass.java:6)
at MyClass.main(MyClass.java:3)
輸出的第一行是toString()方法的輸出,后面幾行的內容都是之前通過fillInStackTrace()方法保存的內容。關于這個方法,我們后面會講。
下面看一個例子:
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()中調用方法f(),在main方法中捕獲異常,并且打印棧軌跡信息。因此,輸出依次展示了f—>g—>main的過程。
2.getStackTrace()方法
這個方法提供了對printStackTrace()方法所打印信息的編程訪問。它會返回一個棧軌跡元素的數(shù)組。以上面的輸出為例,輸出的第2-4行每一行的內容對應一個棧軌跡元素。將這些棧軌跡元素保存在一個數(shù)組中。每個元素對應棧的一個棧幀。數(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出去。在上一級的方法調用中,再捕獲這個異常并且打印出棧軌跡信息。這兩個棧軌跡信息會一樣嗎?我們看一下代碼:
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()方法中拋出的,按理說這兩個打印棧軌跡的信息應該不同,第二次打印的信息應該沒有關于f的信息。但是事實上,兩次打印棧軌跡信息是一樣的。輸出結果如下:
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)
也就是說,捕獲到異常又立即拋出,在上級方法調用中再次捕獲這個異常,打印的棧軌跡信息是一樣的。原因在于沒有將當前線程當前狀態(tài)下的軌跡棧的狀態(tài)保存進Throwabe中。現(xiàn)在我們引入fillInStackTrace()方法。這個方法剛好做的就是這樣的保存工作。我們看一下這個方法的原型:
public Throwable fillInStackTrace()
這個方法是有返回值的。返回的是保存了當前棧軌跡信息的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();
//不要忘了強制類型轉換
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相關的信息了。
以上就是關于Java棧軌跡的一些我之前沒有掌握的內容,記下來備忘。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
- JAVA中StackOverflowError錯誤的解決
- JVM---jstack分析Java線程CPU占用,線程死鎖的解決
- Java中使用StackWalker和Stream API進行堆棧遍歷
- Java StackTraceElement實例代碼
- Java線程Dump分析工具jstack解析及使用場景
- 深入分析JAVA Vector和Stack的具體用法
- java中stack(棧)的使用代碼實例
- Java反射之Call stack introspection詳解
- Java數(shù)據(jù)結構與算法之棧(Stack)實現(xiàn)詳解
- OneinStack一鍵安裝PHP/JAVA/HHVM和超詳細的VPS手動安裝LNMP的方法
- java自帶的工具Jstack截取進程中的堆棧信息
- Java 并發(fā)編程ArrayBlockingQueue的實現(xiàn)
- 詳解JAVA中priorityqueue的具體使用
- 詳解Java中的延時隊列 DelayQueue
- 詳解java中DelayQueue的使用
- java隊列之queue用法實例分析
- Java多線程工具篇BlockingQueue的詳解
- Java Stack與Queue詳解
相關文章
java中catalina.home與catalina.base區(qū)別點整理
在本篇文章里小編給大家整理的是關于java項目中catalina.home與catalina.base區(qū)別點,需要的朋友們可以學習下。2020-02-02
Java動態(tài)修改配置即時生效的方式WatchService
這篇文章給大家分享了Java動態(tài)修改配置即時生效的方式WatchService的相關知識點內容,有興趣的朋友可以參考學習下。2018-06-06
Spring?Boot?3?整合?MinIO?實現(xiàn)分布式文件存儲的全過程
本文介紹了如何使用SpringBoot3和MinIO實現(xiàn)分布式文件存儲,通過MinIO的分布式對象存儲系統(tǒng),可以解決傳統(tǒng)單機文件存儲方案在面對大規(guī)模數(shù)據(jù)和高并發(fā)訪問時的不足,文章詳細講解了MinIO的安裝、配置和使用,感興趣的朋友一起看看吧2025-03-03
Java File類 mkdir 不能創(chuàng)建多層目錄的解決
這篇文章主要介紹了Java File類 mkdir 不能創(chuàng)建多層目錄的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
Java數(shù)據(jù)結構之環(huán)形鏈表和約瑟夫問題詳解
約瑟夫(Josephus)問題是單向環(huán)形鏈表的一種體現(xiàn),也就是丟手帕問題,下面這篇文章主要給大家介紹了關于Java數(shù)據(jù)結構之環(huán)形鏈表和約瑟夫問題的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-08-08

