Hi,你好。下面我们一起来学习异常处理。
异常呢,Exception,又称为呢例外、差错、违例等等。
这种异常呢它实际上是java运行错误处理的机制,也就是说
我们把这种处理错误的机制呢,里面的对象就称为 异常。其实这个异常呢它的基本写法还是很简单的,
它的基本的写法是这样的,try,试图做一些事情,啊,这里面一系列语句,
然后这个方括号外面呢再写catch。就是在试图做这一系列语句呢, 如果遇到错误我就捕获到这种错误,捕获到这个Exception,
就这种异常,然后呢,捕获到异常处理以后呢, 再进行各种处理。我们下面看一个例子,请看这个例子。
在这里面呢,我们用到了一些关于输入和输出的一些
类,那这里输入和输出类呢就是我们最早学过关于输入输出的类,它写起来呢比较麻烦。
它就是BufferedReader,然后里面呢又有一个Inputstream,
最后还用了System.in,这里面的这一系列语句它的写法呢我们基本固定就这么写- 就行了。
那在这里面就有一个问题,我们在输入输出的时候, 那由于可能用某种设备或者某种原因
让输入、输出出错了,所以这里面呢就有可能有错误。
下面呢我们这里面再in.read来,就这个对象呢
输入输出读一行,那它也可能遇到错误。还有呢,我们下面把这个得到这一行呢字符串
把它用integer.parseInt解析成一个整数, 那也有可能呢这个字符串它解析成整数呢,也可能出错。
那么这一系列的事情它都有可能出现各种异常,
然后我们怎么去处理它呢?我们try,试图地去做这件事情,然后catch。
在这里面就是说捕获到IO的异常,输入输出,就input/output异常,
就是输入输出异常。那然后我们做一些处理,当然这里呢我们只是简单地
printStackTrace,显示这个调用堆栈的一些情况。
这是供我们这个,供我们调试的时候使用。下面还有一个异常呢就是
在这一段呢我们还可以再写一个catch,再捕获另一种异常, 就是NumberFormatException,就是数字的格式异常,
所以也就是说我们在试图做这一段的程序的时候呢,我们可以 捕获这两种异常,这是最基本的异常的写法,
所以它还是比较容易理解的。
那么这种理解机制我们,这种错误处理机制我们可以对比一下传统的语言,比如说像C语言等- 等一些语言,
它在错误处理的时候呢是怎么处理的呢?它要在不同的地方 用if语句来判断,用各种if语句来判断是否出现例外。
另外呢,这种判断呢又到处都可能出现异常,所以它用一个全局变量,比如说ErraNo
来记住出现的错误。那这种机制呢,也有一些缺点。第一个呢是正常或异常处理的
代码,同样地都写到一起,就是正常异常我们都写到一起,其实呢像有一些,像我们刚才那个
数字解析不了等等,或者是IO的异常,这种异常呢它实际上
它不是一种正常流程,也就是说我们把正常或异常都写到一堆,
写到一起,然后用if语句判断,这样的可读性很差,因为我们把正常的东西呢 就和异常混到一起了,所以不好。
另外一个呢就是,我们在异常,因为各种情况都会出现,所以呢这个到处都要进行这种错误- 的检查。
所以它的可维护性也很差。另外呢,还有一个错误由谁来处理,是由底层的函数来处理呢- ,还是用
上层的函数来处理呢?还是用main函数来处理呢?职责不清, 所以这是一些。在java呢你看它把这几个问题呢都解决了。
那么它在处理异常的时候呢,
它有几个基本的流程,第一个呢是抛出异常,就是我们
在一个某种情况下,比如说出了问题我们可以用底层函数来抛出异常,
这个输入,抛出异常。那这个异常抛出呢, 它不一定在我这个函数里面去处理,我们只管抛出,那
这个谁来处理呢?这个运行系统自动地在这个调用堆栈里面去查找,
所以我们说调用站,所谓的调用站呢就是一层函数再调一层
函数,这个函数里头又调函数,一层一层地这么调用,这个称为调用站。
那么呢它会自动地去查找,一直回溯,就是说我这一层没有找到
来处理的,然后继续往上一层地调用,再一层一层地去找,直到
找到什么呢?直到找到下面一步就是说,哪个地方写了catch语句,捕获语句,
也就是说,这样的话我们是又在某些地方抛出,然后呢
运行系统呢,它会自动去查找,直到找到这个捕获的地方为止。
所以这样的话,我们就是把抛出异常和捕获异常呢
它实际上职责得分得很清楚了。而这个处理机制呢, 就要比我们传统的要好。这里面有几个相关的语句,
一个是抛出异常呢就是throw,一个异常的对象,这是个异常对象的实例。
然后捕获异常呢,它就是try,然后 这语句组,然后我们刚才已经看见了catch,这个catch呢,这里要写一个异常,什-
么样的异常?然后呢, 这个参数有个名字,然后我们就可以对它进行处理,另外呢,
还有别的异常我们再写一个catch。另外还有一个语句呢,就是子语句呢 叫finally,就最终的,这个最终的意思呢就是说
我不论是有没有异常,不管有异常也好,没有异常也好,
我们都在这里呢执行最终的一些语句,就这个叫finally。
那在写的时候呢,catch后面有多,可以有0到多个,finally呢,可以有0到1- 个,就是这样的。
那异常这里面 各种异常我们刚才看到有IO的异常,有数字格式的异常,
那具体来看一下这个异常分类。Java里面的异常呢,它的最,
最上面这一层的这个父类叫Throwable,可抛出的,这个。
这里面分到两类,一个叫Error类,就是错误类,这个错误呢主要是指虚拟机方面的错误,
我们应用程序呢一般不去管它,我们说的异常呢,平时所说的异常呢 都是指的Exception,及其子类。那这个Exception呢,
它里面又有一系列的,包括我们刚才看到的IO的异常,还有其他的
一些异常等等。所以这样的话,就有一系列的异常。
异常这个,Exception这个类呢是我们
其他异常类的子类,这个异常类,异常类的父类,这个
类呢它有一个构造方法就是不带参数,还有一个带一个信息message,
另外呢,比较常用的还有一个呢带一个信息,同时呢带一个
cause,就是原因,就是我这个异常对象它的底层原因是
什么,这样的话,这是很好的一个做法。实际上把,就是我们这个原因呢 它跟底层的原因我们也把它作为参数放到里头,
到时候就可以用getMessage来得到它的这个信息, 然后呢getCause呢得到它的内部原因,就是实际上内部原因又是一个throwab-
le,又是个 可抛出的一个对象。然后还有我们刚才也看到个printStackTrace,
就是显示呢这个调用堆栈的一些跟踪信息, 就是我们说它是哪一层调到哪一层函数,又调到哪一层函数,在哪一层
处理异常,这样呢我们可以,就是给 程序人员呢可以观察它是什么原因。
另外呢我们也有多个异常,多个异常呢它在写多个catch,这个时候呢,有一个要求,
就是在多个catch呢,它是从上到下的这么执行,
所以在我们写的时候有一个基本要求,就是子类的异常要写到这个父类异常的前面,
也就是说,更具体的,子类就是更具体的, 那么写到父类的前面,比如说我们有一个输入输出异常,那
文件位找到异常呢就是更子类就是更 具体的,就文件位找到它是输入输出异常,那么
finalofbound这个异常呢就要写到IO的Exception的前头,这是一个- 语法规范。
另外对于final语句呢要注意,就是它是无论
是否有异常,都要执行,所以呢,也即使是这个里面
这个try里面,试图执行里面有break也好,return也好, 只有这些语句呢,它都会
执行这个finally这一个方括号括起来的语句。
所以呢,这个它的执行呢实际上 final这个语句也就是不管正常还是异常,实际上它会
生成多遍。我们下面看一个例子。
这个final语句呢,为了加深理解,我们看一个简单例子,就是这个 int
a=100,然后a,try里面a=200, 然后catch呢有几个异常,就是这里呢
我们就写了两个异常,这是,这是,比如说,IndexOutOfBound 就是索引下标超界了,当然我们这里实际上没有这种异常了,
我们故意这样写的。然后有条语句,300, 然后呢在这个普通的异常呢,是更普通的异常要写到下头,400,然后finally写个-
500, 然后呢最后写个a=600。那我们把这个呢,反编译一下,看看它生成的代码,你会发现
什么情况,这个作为了解。我们知道java p-c它可以呢反编译这个代码,
我们可以看看这里面的代码。它是a 100,然后呢,
这里的情况呢,a=100,然后呢有200,最后呢你会发现呢这个500,
a实际上就是finally,finally最后500。然后如果等于300这种情况呢,
它后面又写了个500,它又写了个500,所以呢,相当于就是说
它这里面生成了多次这个final。所以也就是说不管它怎么个情况, 它这个final里面的语句呢500都会执行。另外我们也注意
到它后面有一个所谓的异常表,Exception table,
这里面说哪一条指令到哪一条指令呢,它是捕获哪个异常, 所以你看它这里实际上从哪儿到哪儿,然后呢,
捕获了异常以后直到哪儿,实际上它有一个表,这个表呢实际上就是我们说的异常表,
那就catch。但是如果这里面呢除了异常,这没有这个异常表找不到这样的捕获异常的,
它就会调用它的父类,啊,调, 如果找不到这个异常表呢,它就会到它的,调用那个函数
里面再去找,如果还找不到,一直往上找,直到最后找,找 找到哪个,找到main函数,找到main函数。
当main函数如果还没有处理异常,那这个 虚拟机就没法执行了,它就异常了,中断了,虚拟机
就结束了。我们下面,这刚才是看那个原理,我们下面再看一个例子。
请看这个例子,这个例子呢我们上面有一个复函数,这个函数呢 我们用1去调用它,请我们来分析一下
它会是一个什么样的情形,然后我们再验证一下。
你看,假设这个int呢参数是为1,那么1呢到这里面有个try catch,
这个try呢里面如果等于1的话,输一个new Exception,它就抛出了异常, 那么也就是说在某种情况下呢,它向外抛出异常。
但是呢由于我们这个函数呢是一个实验性函数,因为它自己呢又处理了这个异常,
所以它抛出以后呢,它自己捕获到了,所以这个捕获到这个异常呢, 再output呢,就+=2,所以呢,
它里面呢,这个,这个字符串里面呢,多了一个2。
然后又写return,注意这个return呢
它这个return呢它意味着我们就要返回了,但是呢
要注意到它这里有个finally,啊,有个finally,你即使不管你是用retu- rn也好,
这个finally总是要执行的,总是要执行的。所以呢, output呢加等于了3,啊,+=3。所以,
它这个output的结果呢就是2,3,我们运行一下看是不是这样的。