[空白_录音] [空白_录音]
嗨,同学你好,下面呢我们说一下异常处理。
异常呢,实际上就是当程序发生某种
特定的情况,我们需要呢交给 另一个调用者来处理。
这种情况呢, 就是异常。
一般的情形呢,它是这个样子的: 就是 Try
试图,执行某段代码,这试图执行某段代码。
然后呢,在这个过程当中, catch 捕获的某种异常,嗯,捕获的某种异常。
这个异常呢,是一个对象,里面含有异常的信息。
这个变量 e ,然后就可以在这里面进行处理。
就是说试图做 这个程序、 做这件事情。
如果没有异常呢?当然就执行下去了。
如果中间遇到了异常,它这个程序流程呢,又捕获了异常,就程序就走到这个 catch 这个分支里头来了。
在有的时候呢,还有个 finally,finally 的意思就是说在执行这段代码
不论是有异常,还是没有异常,都走到这个里头来。
甚至包括这个 try 里面有 return。
所以它也会走到这个 finally 这个、 这个分支里头来。
总的来说呢, 它的基本结构就是: try、 catch、 finally。
就是试图做事情 如果出异常,就到这儿。
如果,不论出异常还是不出异常 都走进 finally。
但 finally 这一段呢,是可以没有的。
可以是,嗯,没有的。
那么这里的 捕获的异常,它都是 System.Exception 类 及其子类。
这个类呢,它里面有一个 呃,有几个构造方法,一个简单的就是不带参数。
更一般的情形呢,是带一个信息,嗯,这样的一个异常,这个对象。
它里面有一个重要的属性叫 message,我们可以呢 知道它当时的这个异常的信息。
还有一个 StackTrace 就是它的调用iii,那就是这个调用的过程,
在哪个地方出了异常?我们可以通过它能够找到那些异常的一个发生的地点。
这是异常的概念,呃,系统里面定义了 很多异常,比如说:
System 这个命名空间有 OutOfMemory 就是内存用光了, StackOverflow
等等,还有一个比较常见的 IndexOutOfRange 就是下标超界了,
下标超界了以后,它会发生一个异常,比如说数组的下标超界了。
或者我们从字符串里面取,本来只有五个字符,你取了一个。
嗯,下标为五的,因为下标只能零到四,所以这种叫 最经常的一个
IndexOutOf,嗯,当然还有一个也比较常见的 NullReference。
就是,Null 指针,呃, Null 引用。
那么 Null 引用呢,如果一个对象是 Null 你然后后面 Null
这个对象点什么什么,那它就会抛出这个 异常等等吧,有很多异常。
还有 DivideByZero 就是除以零。
整数除法里头不能除以零,如果除以零 的话,它就会抛出这个异常。
一般的情形呢, 捕获异常呢,就是 try 什么什么 catch ,里面可以有好多异常。
那,它有一个要求就是:如果更一般的,假设前面是
呃,一个指令,就是更具体的,那么更一般的呢要放到后头。
我们举个例子,比如说: 有普通的任意的异常,要写到最后面, 就是 Exception。
如果有 IO 的异常,就是输入输出的异常,要写到前面一点。
再更细节的,FileNotOpen 就是文件打不开,这个异常, 那就是写到。
你说,越具体的,或者越指类的,要写到前头。
越一般的,要写到后头。
这是语法要求。
然后 Finally 呢,可以是没有的。
但是如果还有一种特殊情形,就是说我所有的异常 我都捕获那个
catch 后面补写这个参数,补写这个圆括号或参数。
那就是捕获所有的异常。
当然这个,这种捕获呢? 是比较少的,因为你没有这个对象的信息,你没法 知道当时的什么异常。
所以异常呢还是比较好理解的,用起来很方便。
呃,如果我们在一个程序里面,要强制抛出异常,或者是抛出一个 异常给调用者。
就是说我们遇到某种情况我们处理不下去了,这个时候呢,
我要做的事情呢,是把这个异常抛出来,给调用者他来去 try catch 他来去处理。
所以,这个程序抛出异常,那就用一个 throw
这个关键词,一个异常,一般的情形是在某种情形下 if
怎么怎么样 在这种情况比如说:我们文件打不开了,或者我们说数据库
要查询的记录查不到了等等,那么这个时候就抛出一个 new 一个什么什么异常。
并且这个异常它是一个对象,所以经常的有个构造的构造的这样一个 异常的对象,经常还代表些信息。
根据这个异常的那个情况。
如果用户要创建自己的异常呢?一般情况下我们应该从 Exception
或者 ApplicationException 来继承。
当然要定义一些 构造方法,然后里面呢还有一些普通的属性、 方法等等。
有的时候我们把一个异常要重新抛出呢,叫 throws
直接抛出去,那就不写,这个, 这个类,这个这个对象。
在异常的 自定义我们的异常的时候呢,一般情况大家是有一个默认的。
或者说,一个惯例。
大家经常这样做。
就是说,我在 new 一个异常的时候, new 某某某异常的时候,
在这个异常呢,一个是给,要给一个信息。
同时呢,还有把它 内部的这个异常,就是更底层的异常,把它,也把它
放到这个构造方法里头。
它的目的呢是说,我这是一个 高级的异常,但是它更低层的一个异常呢,我也把这个信息呢 告诉给这个对象。
这样的话这个内部异常呢,我们可以通过 InnerException 这个 属性来访问到内部的异常。
这样的话也就,一个异常它底层是什么原因造成呢?比如说
我们说银行里面取钱,取不到了,它底下的异常呢,它可能
是一个,这个,印钞机出问题了,那这是底层的异常。
印钞机这是一个异常 印钞、 这个这个出钞机呀,这个这个异常呢
它有可能是底层的再细节的,比如说:它是哪个零件坏了?
所以这样一层一层的,我们称为叫异常的链接,就相当于我们从
表层的异常一直可以深入挖掘下去,到最底层 的异常。
所以这个观点就是相当于我们在定义自己的异常的时候
最好有一个构造方法,它是把底层的这个异常,也把它 嗯,也把它保存下来。
我们看一下这样的一个例子: 这里呢,是一个例子,那比如说我们有一个
关于银行 ATM 机这样的一个模拟系统。
那它这里呢有一个 数据库异常,数据库异常呢它是继承了 Application
异常,是一个冒号来继承了,这里有一个构造方法。
是是这样一个构造方法,但这个构造方法呢,一般我们都要调 base, 基础类的构造方法。
在这里呢有一个我的应用程序的 方法,这个异常,我的应用的一个异常。
这个应用的异常呢, 它就定义了,除了定义一个普通的指代信息的构造方法以外,
还定义了一个呢,就是 MyAppli MyApp
异常呢,它定义了个,再带一个参数 inner 异常
这样呢,我们把这个内部的这个异常呢,也把它记下来。
所以定义这个异常是一个很好的一个习惯。
那我们看程序在用的时候是这样, 比如说这个 ATM 机,它要做某种事情,
比如说,要得到那个信息,要得到这个银行账户余额 信息,某个账号的余额信息。
那我们在这里呢,试图 得到,如果是有异常,那我们就要写
这个异常,它是什么异常,这个 e.Message,然后我们也可以
通过 e.InnerException 就是内部异常的 Message,它内部的原因是什么?
这样是一个链接的关系,就,这个异常,它内部,还有个异常。
那我们在做的时候呢 它是怎么发出这个异常的呢?你看啊,就是在这里,
比如说,我们要得到账户余额信息, 然后我们就从呢,这个数据库里面去找到这个信息。
在这个过程当中, 我们去找,试图去找,如果没找到呢,我们要抛出一个数据库异常。
数据库异常,在这里呢, 我们对外发出的时候,是
new 了一个 应用的异常,说,账号不存在。
这个 e 呢,是内部的原因。
那内部原因有可能是什么文件iii等等,总的说来一句话:
就是我们在抛出一个异常的时候,制定一个异常的时候,
尽量把内部的异常,也把它的信息存下来。
好,这就是关于 异常的一个应用。
还有一个细节,那就是算术溢出。
但是这个异常呢,它是比较特殊的。
它可以用编译器呢,进行检查。
啊对对就是,用编译器呢,根据不同的情况编译成不同的 这个代码。
那么一种呢,就是,他会检查这个 算术溢出。
比如说两个数,整数,太大了,一加,超出整数的范围了,
它就会,它就会根据情况,那么超范围算异常呢还是不算异常呢?
在编译的时候,就有一个选项:叫做是否检查 算数异常,所以这个算,算数溢,是否检查 算术溢出的异常。
然后我们用命令行呢是这样,如果使用 visual studio 呢 它有个编译的,有一个属性。
嗯,就是点右键项目有个属性。
就可以选这个,是否,是否进行算术溢出的检查? 总的说来呢,
C# 当中的异常呢,还是比较好用的。
就是 try 什么什么 catch 什么什么 然后 finally 什么。
如果是要制定异常呢,最好是 定义一个内部,把内部的异常这个构造方法,
要定义出来。