zoukankan      html  css  js  c++  java
  • try、catch、finally详解,你不知道的异常处理

    介绍

    不管是新手还是工作几年的老油条,对try{}catch{}来说是不陌生的。他可以来帮助我们获取异常信息,在try中的代码出现错误,火灾catch代码块中被捕获到。官方也给了详细的解释:。

    抛出异常时,公共语言运行库(CLR)会查找catch处理此异常语句。如果当前正在执行的方法不包含这样的catch块,则CLR会查看调用当前方法的方法,依此类推调用堆栈。如果未catch找到任何块,则CLR向用户显示未处理的异常消息并停止执行该程序。

    以上的这些基础我们可能都了解。但是你真的了解他的运行步骤吗?我就是带着这个疑问进行了一下的几个测试。

    简单示例:

    既然有了疑问就带着疑问想办法验证吧,下面我们通过多个例子来一步一步的分析得到我们想要的结果。

    简单的try catch

    首先是一个try中没有异常的示例:

     static void Main(string[] args)
            {
                string result =GetStr();
                Console.WriteLine(result);
                Console.ReadLine();
            }
            public static string GetStr()
            {
                try
                {
                    Console.WriteLine("走到:try");
                    return "这里是try返回值";
                }
                catch (Exception e)
                {
                    Console.WriteLine("走到:catch");
                    return "这里是catch返回值";
                }
                finally
                {
                    Console.WriteLine("走到:finally");
                }
                return "这里是方法底部返回值";
            }

    运行结果:

    执行分析:

    这是最简单最常见的示例,没有发生异常,然后没有走catch,执行顺序是try=>finally=>return;

    所有我们得到一个还不确定的结果在GetStr方法中不会执行方法自己的return;

    但是finally方法块都会执行;

    来个异常的:

    下面我们让try方法块出错就好了,然后我们修改一下代码如下:

     public static string GetStr()
            {
                try
                {
                    int value = 0;
                    Console.WriteLine("走到:try");
                    var i = 1 / value;//这里会出错 0不能被整除
                    return "这里是try返回值";
                }
                catch (Exception e)
                {
                    Console.WriteLine("走到:catch");
                    return "这里是catch返回值";
                }
                finally
                {
                    Console.WriteLine("走到:finally");
                }
                return "这里是方法底部返回值";
            }

    运行结果:

    执行分析:

    这里在try发生了异常,然后没有正常返回,进入到了catch方法块:try=>catch=>finally=>return;

    这里我们可以确定:

    • 不管try有没有出错finally方法块都会被执行。【快记笔记,知识点。】
    • 就算try和catch方法都有return,finally都会执行;
    • 只要try或者catch return返回,try catch 之外的return都无效;

    说到这里有些不懂得人可能会有疑问?那在finally写个return是什么结果哪?很不幸的告诉你,不能这么写,写了会怎么样,哼会提示:控制不能离开finally子句主体;

    验证return的值

    上面我们知道了怎样都会执行finally,但是执行了finally对我们的正返回值有没有印象哪,例如我在try里面对一个变量赋值为a字符串,进行了返回,但是在finally里面修改成了b字符串。会不会被修改哪?

    我们还是老代码,然后修改成我们想的样子:

    public static string GetStr()
            {
                string str = "";
                try
                {
                    str = "修改成了a";
                    Console.WriteLine("走到:try");
                    // return "这里是try返回值";
                    return str;
                }
                catch (Exception e)
                {
                    Console.WriteLine("走到:catch");
                    return "这里是catch返回值";
                }
                finally
                {
                    str = "修改成了b";
                    Console.WriteLine("走到:finally");
                }
                return "这里是方法底部返回值";
            }

    运行结果:

    执行分析:

    没有异常还是老样子:执行顺序是try=>finally=>return;

    但是我们在finally修改了str字符串,但是通过输出结果我们得到的还是a字符串,

    所有我们得到结论:虽然finally方法会被执行但是,返回结果不会被改变,也就是如果finally是在return之后执行的那么他会把返回结果先保存起来,然后不管finally代码执行了什么,都不会影响到返回结果,等finally执行完成在返回结果。

     多个重复try

    那么我们可以写多个try{}try{}这样的语句吗?不行,会直接报错,其实这样写没有任何意义。

    多个重复catch

    那么重复多个catch哪?这个是可以的例如下面我这样:

      try
                {
                    str = "修改成了a";
                    Console.WriteLine("走到:try");
                    // return "这里是try返回值";
                    return str;
                }
                catch(InvalidCastException e) {
                }
                catch (Exception e)
                {
                    Console.WriteLine("走到:catch");
                    return "这里是catch返回值";
                }

    这个是被允许的,因为这是有意义的写法。

    开始升级

    为什么要一定写try-catch-finally 我只写其中一部分不可以吗?

     try-catch

    那么我们这次不写finally试一试吧。try方法块没有异常已经不用测了,因为上面我们已经确认过了。会返回try的内容。那么就try异常吧。

     public static string GetStr()
            {
                try
                {
                    Console.WriteLine("走到:try");
                    int value = 0;
                    int s = 1 / value;              
                    return "这里是try返回值";
                }
                catch (Exception e)
                {
                    Console.WriteLine("走到:catch");
                    return "这里是catch返回值";
                }
                return "这里是方法底部返回值";
            }

    运行结果:

    执行分析:

    通过可以正常运行我们知道这样写一点问题都没有,所以结果就是

    • finally也不是必须的。
    • 如果catch没有return 就会返回底部return方法。这是我们的常识。

    这样做有什么作用或者意义哪,通常我们可以上面说的定义多个catch来检测异常,还有一个用途就是忽略异常,就是这种异常你系统可以被运行,就可以catch内不写return正常跳过异常执行下面的方法体。但是不是很被建议,

    try-finally

    那么try-finally哪,这样写也是被允许的。

    这样单独写第一就是在finally语句块内做try的资源释放。正常情况下try没有异常,在finally中处理try语句块的资源释放。

    第二就是try发生了异常,其实finally起到的作用还是一样的。但是这里区别在于如果异常未经处理,可能就导致程序退出了。所有执不执行已经无所谓了。我们来个异常示例:

    static void Main(string[] args)
            {
                string result = "";
                try
                {
                    result = GetStr();
                }
                catch (Exception e)
                {
                    Console.WriteLine("主方法catch:");
                }
                Console.WriteLine(result);
                Console.ReadLine();
            }
            public static string GetStr()
            {
                try
                {
                    Console.WriteLine("走到:try");
                    int value = 0;
                    int s = 1 / value;               
                    return "这里是try返回值";
                }
    
                finally
                {
                    Console.WriteLine("走到:finally");
                }
    
                return "这里是方法底部返回值";
            }
    View Code

    运行结果:

    执行分析:

    try发生了异常,但是因为finally始终都会执行所有也会执行,然后异常被调用方法内的catch捕获执行顺序:try=>finally=>catch(主方法)

    所有我们得到结果:

    • try-finally可以运行
    • try如果没有catch但是发生异常会向上找catch方法块来捕获。知道没有系统崩溃。

    以上的例子都是非控制(系统出现异常就自动抛出了)的抛出异常,那么我们可以控制异常的抛出点吗?当然可以。

    throw

    还是老习惯先上官方解释,发出程序执行期间出现异常的信号。

    到底什么意思哪,我个人理解就是一个告诉你是不是出现异常的标志,就像信号灯一样,亮了什么颜色就代表着什么意思 ,当然就是打个比方。信号灯一定是对的,但是这个可不是啊。
    简单来总结他就两个功能:第一是告诉别人有异常,第二就是重新发出异常。

    告诉别人有异常

    简单来说就是自己可以定义一个异常,然后给上层代码处理。(我就是在这想告诉你有异常)

    static void Main(string[] args)
            {
                string result = "";
                try
                {
                    Console.WriteLine("主方法try:");
                    result = GetStr(6);
                }
                catch (IndexOutOfRangeException e)
                {
                    Console.WriteLine($"主方法catch抛出异常:{e.GetType().Name}");
                }
                Console.WriteLine("主方法结束");
                Console.ReadLine();
            }
            public static string GetStr(int index)
            {
                if (index < 0 || index > 5)
                {
                    Console.WriteLine("进入异常:");
                    throw new IndexOutOfRangeException();
                }
                return "正确返回";
            }
    View Code

    运行结果:

    执行分析:

    在主方法里调用GetStr方法,然后传入了6判断进入if然后给自己给出了异常,退出当前程序进入主方法捕获异常catch中,捕获到异常打印。这里就展示了自己在某种情况下定义一个异常然后给上层抛出。

    重新引发异常

    这个与上面有什么不同哪,功能都是一样的,但是效果却不一样,这个是我发生了异常但是我不处理,我在继续告诉别人让别人处理。下面我们只需要把上面的GetStr方法修改成这样:

     public static string GetStr(int index)
            {
                try
                {
                    if (index < 0 || index > 5)
                    {
                        Console.WriteLine("进入异常:");
                        throw new IndexOutOfRangeException();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine($"进入异常catch重新抛出异常:{e.GetType().Name}");
                    throw;
                }
                return "正确返回";
            }
    View Code

    运行结果:

    执行分析:

    在主方法里调用GetStr方法,然后传入了6判断进入if然后给自己给出了异常,在GetStr方法内的catch捕获到异常,但是他没有处理,有重新使用Throw来引发异常,把异常传到了上层(主方法),最后还是主方法的catch来处理异常。

    性能的影响 

    try{ }部分和不加try/catch语句块的效率几乎一样, catch{}部分似乎需要100倍以上的时间 ,所以只要不把try{}catch{}作为你的程序的逻辑,这种设计就是合理的。

  • 相关阅读:
    微信小程序在扫一扫进入小程序的时候 安卓手机后台继续运行的常规处理
    在微信小程序上,帮助中心界面实现类似手风琴案例
    使用artTemplate的模板引擎,使用简单
    使用原生JavaScript实现图片预加载,方法简单代码少
    在wepy框架中 使用promise对发送网络请求进行封装 包括post跟get请求
    JavaScript实现按字典排序进行md5加密, 以及个人在小程序也可以实现
    当在微信扫一扫进入小程序 并获取到二维码的参数 从而实现扫码进入小程序
    第九章:看看精彩的世界-使用网络技术
    玉渊潭公园
    军事博物馆
  • 原文地址:https://www.cnblogs.com/yanbigfeg/p/9295541.html
Copyright © 2011-2022 走看看