神奇的 new Exception()


先看下一段代碼:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        test1();
    }

    private void test1() {
        test2();
    }

    private void test2() {
        test3();
    }

    private void test3() {
        test4();
    }

    private void test4() {
        Log.d("wxl", "test", new Exception());
    }
}

你們猜會打印什么?

12-29 12:16:05.479 8140-8140/com.wuxiaolong.myapplication D/wxl: test
    java.lang.Exception
        at com.wuxiaolong.myapplication.MainActivity.test4(MainActivity.java:29)
        at com.wuxiaolong.myapplication.MainActivity.test3(MainActivity.java:25)
        at com.wuxiaolong.myapplication.MainActivity.test2(MainActivity.java:21)
        at com.wuxiaolong.myapplication.MainActivity.test1(MainActivity.java:17)
        at com.wuxiaolong.myapplication.MainActivity.onCreate(MainActivity.java:13)
        at android.app.Activity.performCreate(Activity.java:6975)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

是不是有點神奇,居然把 test4() 方法調用關系給打印出來了,是怎么做到的?是不是有地方把這個每步調用關系給保存下來了。

首先進 Log 看下 Log.d():

public static int d(String tag, String msg, Throwable tr) {
    return println(LOG_ID_MAIN, DEBUG, tag, msg + '\n' + getStackTraceString(tr));
}

public static String getStackTraceString(Throwable tr) {
    if (tr == null) {
        return "";
    }
    // This is to reduce the amount of log spew that apps do in the non-error
    // condition of the network being unavailable.
    Throwable t = tr;
    while (t != null) {
        if (t instanceof UnknownHostException) {
            return "";
        }
        t = t.getCause();
    }
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    tr.printStackTrace(pw);
    pw.flush();
    return sw.toString();
}

這里第三個參數 Exception 是 Throwable 子類,初始化看 Throwable 構造方法:

public Throwable(String message) {
    fillInStackTrace();
    detailMessage = message;
}

/**
 * Fills in the execution stack trace. This method records within this
 * {@code Throwable} object information about the current state of
 * the stack frames for the current thread.
 *
 * <p>If the stack trace of this {@code Throwable} {@linkplain
 * Throwable#Throwable(String, Throwable, boolean, boolean) is not
 * writable}, calling this method has no effect.
 *
 * @return  a reference to this {@code Throwable} instance.
 * @see     java.lang.Throwable#printStackTrace()
 */
public synchronized Throwable fillInStackTrace() {
    if (stackTrace != null ||
        backtrace != null /* Out of protocol state */ ) {
        backtrace = nativeFillInStackTrace();
        stackTrace = libcore.util.EmptyArray.STACK_TRACE_ELEMENT;
    }
    return this;
}

如自己所想,這里 fillInStackTrace() ,正是一步一步地追蹤方法的調用,直到追蹤到線程的終端。fillInStackTrace 是 native 方法,就不往下​跟了。​

Exception 表示可以被程序捕獲並處理的異常錯誤,JVM 用方法調用棧來跟蹤每個線程中一系列的方法調用過程,該棧保存了每個調用方法的本地信息。

對於獨立的 Java 程序,可以一直到該程序的 main 方法,當一個新方法被調用的時候,JVM 把描述該方法的棧結構置入棧頂,位於棧頂的方法為正確執行的方法,當一個 Java 方法正常執行完畢,JVM 會從調用棧中彈處該方法的棧結構,然后繼續處理前一個方法,如果 java 方法在執行代碼的過程中拋出異常,JVM 必須找到能捕獲異常的 catch 塊代碼,它首先查看當前方法是否存在這樣的 catch 代碼塊,如果存在就執行該 catch 代碼塊,否則 JVM 回調用棧中彈處該方法的棧結構,繼續到前一個方法中查找合適的 catch 代碼塊,最后如果 JVM 向上追到了 main()方法,也就是一直把異常拋給了 main()方法,仍然沒有找到該異常處理的代碼塊,該線程就會異常終止,如果該線程是主線程,應用程序也隨之終止,此時 JVM 將把異常直接拋給用戶,在用戶終端上會看到原始的異常信息。

我的公眾號:吳小龍同學,歡迎關注交流,公號回復關鍵字「1024」有驚喜哦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM