• 神奇的 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 + '
    ' + 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」有惊喜哦。

  • 相关阅读:
    uniapp 微信小程序全局分享配置
    友元类
    C++ 关于构造函数和this调用的思考
    opengl 旋转方向
    缓冲区(二)纹理缓冲区+帧缓冲区
    linux 冒号命令,Linux Shell 内建命令:冒号(:)
    cyropto++
    opengl:纹理单元 ( 详解 glUniform1i 和 glGetUniformLocation 的使用)
    git tag
    C++ 友元类
  • 原文地址:https://www.cnblogs.com/WuXiaolong/p/12130952.html
Copyright © 2020-2023  润新知