1.概述
异常:就是不正常。程序在运行时遇到的不正常的情况。
2.异常的由来
问题按照面向对象思想进行描述,并封装成了对象。因为问题的产生有产生的原因、有问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这些信息进行封装。异常就是java按照面向对象的思想将问题进行对象封装。这样就方便于操作问题以及处理问题。
出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系。
其中:
- 严重的问题:用Error类来描述,一般不做针对性处理。
- 不严重的问题:用Exception类描述,可以使用针对性的处理方式进行处理。
无论是Error或者是Exception都具有一些共性的内容,比如:不正常情况的信息,引发的原因等。它们都是Throwable类的子类。
无论是错误还是异常,它们都有具体的子类体现每一个问题,它们的子类都有一个共性,就是都以父类名才作为子类的后缀名。
异常分两种:
- 编译时被检查的异常,只要是Exception及其子类都是编译时被检测的异常。
- 运行时异常,其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。
3.异常的处理
异常体系中的所有类和对象都具备一个独有的特点;就是可抛性。可抛性的体现:就是这个体系中的类和对象都可以被throws和throw两个关键字所操作。
在开发时,如果定义功能时,发现该功能会出现一些问题,应该将问题在定义功能时标示出来,这样调用者就可以在使用这个功能的时候,预先给出处理方式。
如何标示呢?通过throws关键字完成,格式:throws 异常类名,异常类名…
这样标示后,调用者,在使用该功能时,就必须要处理,否则编译失败。
处理方式有两种:1、捕捉;2、抛出。
对于捕捉:Java提供了针对性的语句块进行处理。
try
{
...//需要检测的代码
}
catch(异常类 变量名)
{
...//异常处理的代码
}
finally
{
...//一定要执行的代码
}
捕捉异常的代码示例:
catch (Exception e) //e用于接收try检测到的异常对象。
{
System.out.println("message:"+e.getMessage());//获取的是异常的信息。
System.out.println("toString:"+e.toString());//获取的是异常的名字+异常的信息。
e.printStackTrace();//打印异常在堆栈中信息;异常名称+异常信息+异常的位置。
}
在方法上通过throws关键字声明了该方法可能会出现问题,在方法上声明异常便于提到安全性,让调用者进行处理,不处理则编译失败。
对多异常的处理:
- 声明异常时,建议声明更为具体的异常,这样处理可以更加具体。
- 对方声明几个异常,就对应有几个catch块,不要定义多余的catch块。如果多个catch块中的异常有继承关系,父类异常catch块放在最下面。
- 建议在catch 处理时,一定要定义具体的处理方式,而不是简单的输出语句。
4.自定义异常
因为在项目中会出现特有的问题,而这些问题并未被Java所描述并封装成对象。这时可以对这些特有的问题按照Java对问题封装的思想,将特有的问题进行自定义的异常封装。这个异常,称为自定义异常。
异常的转换思想:当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。
自定义异常的步骤:
- 定义一个子类继承Exception或RuntimeException,让该类具备可抛性。
- 通过throw 或者throws进行操作。
如:在程序中有自定义方法做除法,若除数是负数也视为是错误的,无法进行运算,那么就需要对这个问题进行自定义描述。
class MinusException extends Exception
{
MinusException(String msg)
{
super(msg);//将异常信息传递给父类
}
}
...
int div (int a, int b)throws MinusException
{
if( b < 0 )
{
throw new MinusException("除数不能为负数");
}
return a/b;
}
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。要么在内部进行一次try-catch处理,要么在函数上声明(throws XxxException)让调用者去处理。
自定义异常信息:
因为父类把异常信息的操作都完成了,所以子类只要在构造函数内,将异常信息通过super语句传递给父类。就可以通过getMessage()方法直接获取自定义的异常信息。
自定义异常必须是自定义类继承Exception,因为异常体系有一个特点:异常和异常对象可以被抛出,是因为它们具备可抛性,这个可抛性是Throwable这个体系独有的特点。只有这个体系中的类和对象才可以被throw和throws操作。
throw 和throws关键字的区别:
- throw用于抛出异常对象,后面跟的是异常对象;throw用在函数内。
- throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在函数上。
Exception有一个特殊的子类异常对象RuntimeException运行时异常。如果在函数内抛出异常(及其子类),函数上可以不声明,编译可以通过。如果函数上声明该异常,调用者可以不用进行处理。编译可以通过。
之所以不在函数上声明,是因为不需要调用者处理,当异常发生,希望程序停止,因为运行时出现了无法继续运行的情况,希望停止程序,对代码进行修改。
自定义异常时,若果该异常发生后,程序无法继续运算,则让该异常继承RuntimeException
5. finally代码块
定义一定会执行的代码块,通常用于关闭资源。
try-catch-finally的格式有三种:
1. try-catch
try
{
...
}
catch(Exception e)
{
...
}
- try-catch-finally
try
{
...
}
catch(Exception e)
{
...
}
finally
{
...
}
- try-finally
try
{
...
}
finally
{
...
}
对于第三种格式(try-finally),因为没有catch处理异常,如果try{…}中有throw new Exception(…);,则方法上必须有throws声明。这种情况,如果出现异常并不会进行处理,但是资源一定要关闭,所以try finally集合只为关闭资源。
注意:
- finally中定义的通常是关闭资源代码,因为资源必须要释放。
- finally只有一种情况不会执行:当执行到System.exit(0);时,程序退出,finally不会执行。
当异常出现后,在子、父类进行覆盖时,有了一些新的特点:
- 当子类覆盖父类的方法时,如果父类的方法抛出了异常,那么子类的方法要么不抛出异常要么抛出父类异常或者该异常的子类,不能抛出其他异常。
- 如果父类抛出了多个异常,那么子类在覆盖时只能抛出父类的异常的子集。
- 如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能内部进行try处理,不能在函数上throws。如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。