赞
踩
在Java中,如果某个方法不能采用正常的途径完成它的任务,可以通过另一个途径退出方法。在这种情况下,方法并不会返回任何值,而是抛出(throw)了一个封装了错误信息的对象。需要注意的是,这个方法会立刻退出,并不返回正常值(或任何值)。此外,也不会从调用这个方法的代码继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常状况的异常处理器。
异常有自己的语法和特定的继承层次结构。下面我们先介绍语法,然后再给出有效地使用这种语言特性地技巧。
在Java语言中,异常对象都是派生于Throwable类的一个类实例。
我们来看一个大概的简化图:
可以看出,所有异常都是由Throwable继承而来,但在下一层立即分解为两个分支:Error和Exception
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。这种错误是java虚拟机jvm报出的。如果出现了这种内部错误,无法解决,只能终止程序。
在设计Java程序时,要重点关注Exception层次结构。
派生于RunTimeException:一般是由编译错误导致的异常(但是编译可以通过,既不用捕获也不用抛出,只是在运行时程序检测出,然后停止),常包括以下问题:
如果出现RunTimeException异常,那么一定就是你的问题
不是派生于RunTimeException的异常属于其他异常,常包括以下问题:
Java语言将派生于Error类或RunTimeException类的所有异常称为非检查型异常,所有的其他的异常称为检查型异常。
编译器将检查你是否为所有的检查型异常提供了异常处理器
如果遇到了无法处理的情况,Java方法可以跑出一个异常。这个道理很简单:方法不仅需要告诉编译器将要返回什么值, 还要告诉编译器有可能发生什么错误。
要在方法的首部指出这个方法可能抛出一个异常,所以要修改方法的首部,以反映这个方法可能抛出的检查型异常,例如:
public FileInputStream(String name) throws FileNotFoundException
当然也可以声明多个异常,例如:
- class MyAnimation{
- ...
- public Image loadImage(String s) throws FileNotFoundException, EOFException{
- ...
- }
- }
自己编写方法时,不必声明这个方法可能抛出的所有异常。至于什么时候需要在方法中使用throws子句声明异常,以及声明出哪些异常,我们需要记住下面的几点:
现在我们假设一个名为readDate( )的方法正在读取一个文件,文件首部包含以下信息,承诺文件长度为1024哥字符。(这里使用的例子是有关IO流的内容,不懂得可以先可以自己查API文档)
然而,读到了733个字符之后文件就结束了。这时候并不是一个正常的情况,需要抛出一个异常。阅读API文档以后,我们发现EOFException很合适,它的描述是:指示输入过程中意外遇到了EOF。
来看示例代码:
- String readDate(Scanner in) throws EOFException{
- ...
- while(...){
- if(!in.hasNext()){
- if(n < len)
- throw new EOFException();
- }
- }
- return s;
- }
我们来总结一下:
throw new Exception();
在这个情况下:
有时候我们可能会遇到任何标准异常类都无法描述的问题。这种情况下,我们就需要创建自己的异常类了。
以下为创建异常类的标准步骤:
以下为示例代码:
- class FileFormatException extends IOException{
- public FileFormatException(){}
- public FileFormatException(String gripe){
- super(gripe);
- }
- }
我们来看示例代码:
- String readDate(BufferedReader in) throws FileFormatException{
- ...
- while(...){
- if(ch = -1){
- if(n < len){
- String description = "String not accepted long enough";
- throw new FileFromatException(description);
- }
- }
- }
- }
其中:
- String description = "String not accepted long enough";
- throw new FileFromatException(description);
就是在描述这个异常的信息
如果发生了某个异常,但没有在任何地方捕获这个异常,程序就会终止。所以我们就需要捕获异常。
要想捕获异常,我们就要使用try/catch语句块,如下所示:
- try{
- ...
- }catch(ExceptionType e){
- //handler for this type exception
- }
当然我们也可以捕获多个异常:
- try{
- ...
-
- }catch(ExceptionType e){
- //handler for this type exception
- }catch(ExceptionType e){
- //handler for this type exception
- }
- ...
抛异常:即前面提的声明异常:throws
捕获异常:try/catch
那该什么时候抛异常,什么时候捕获异常呢?
请记住:
如果你知道该怎样处理这个异常,就去捕获他。如果你不知道怎样该处理这个异常,那就去先抛出他,让他的调用者来捕获
我们也得记住,最后在main( )方法中,我们必须捕获抛出的异常,如果继续抛异常,那么就会把这个异常交给虚拟机,就相当于了Error
可以在catch子句中抛出一个异常。通常,希望改变异常的类型时会这样做。
这样很有道理,如果开发了一个供其他程序员使用的子系统,可以使用一个指示子系统故障的异常类型。
来看示例代码:
- try{
- //access the database
- }catch(SQLException e){
- throw new ServletException("database error" + e.getMessage());
- }
我们也可以把原始异常设置为新异常的“原因”:
- try{
- //access database
- }catch(SQLException original){
- var e = new ServletException("database Error");
- e.initCause(original);
- throw e;
- }
捕获到这个异常时,可以使用下面的这条语句获取原始异常:
Throwable original = caughtException.getCause();
代码在抛出一个异常时,就会停止处理这个方法剩余的代码,并退出这个方法。
但是,如果这个方法已经获得了只有它自己知道的一些本地资源,而且这些资源必须清理,这就会有问题。
finally子句可以解决这些问题:不管是否有异常被捕获,finally子句中的代码都会被执行。
我们来分析一个代码
如下所示:
- var in = new FileInputStream(...);
- try{
- //1
- //2
- }catch{
- //3
- //4
- }finally{
- //5
- in.close();
- }
- //6
try-with-Resources语句(带资源的try语句)的最简形式为:
- try(Resources res =...){
- //work with res
- }
try块退出时,会自动调用res.close( )
我们来看一个典型的例子:
这里要读取一个文件的所有单词
- try(var in = new Scanner(new FileInputStream("/xx/xxx/xx/xx"),StandardCharsets.UTF_8);
- var out = new PrintWriter("xxx.xxx",StandardCgarsets.UTF_8)
- ){
- while(in.hasNext())
- System,out.println(in.next());
- }
不论这个块如何退出,in和out都会关闭。就好像finally子句中调用了in 和 out 的关闭方法
如果try块抛出一个异常,而且close( )方法也抛出一个异常,这就会带来一个难题。try-with-Resources语句可以很好地处理这种情况。原来的异常会重新抛出,而close( )方法抛出的异常则会“被抑制”。这些异常将自动的捕获,并由addSuppressed( )方法,它会生成从close( )方法抛出,并被抑制的异常数组。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。