一、异常
1、异常介绍
异常:程序在运行过程中出现的一些非正常现象。在开发中需要针对不同的异常给出解决方案。
在Java中使用Exception表示异常。
程序运行过程中出现的问题Java将其分类两大类:
- Error:如果程序中出现的XxxxYyyyError,那么表示程序出现重大错误,是需要修改源代码,没有补救的机会,。
- Exception:表示程序中出现一些可以解决的问题,在编程时可以提前给出一些解决的方案。
2、异常的体现
3、异常的解决
在进行异常预先处理的时候,需要确定两个角色问题:
- 方法的定义者:在定义方法的时候,如果方法中的确有异常会出现,这时异常不需要调用者知道,这时需要手动在方法中将异常给捕获住(内部消化异常),如果异常必须抛给调用者,一定要在方法内部使用throw 关键字将异常对象抛出,并且如果该异常是编译时异常,要在方法头部用throws声明该异常。
- 方法的调用者:被动调用别的方法,如果被调用的方法中抛出了异常,自己属于被动的在接受异常, 这时处理方案也有两个:在调用的代码地方捕获,或者自己继续抛出(即在所处的方法中声明该异常,如果是main()方法,则由JVM处理)。
异常的解决方案:
- 捕获异常:
try{
可能出现异常的代码
}catch( 异常类名 变量名 ){
处理异常
} - 抛出异常:
在判断的地方发现可能有异常,使用throw关键字抛出异常,在方法定义的位置需要使用throws来声明方法中抛出的某些异常。
4、异常处理举例
4.1、方法的定义者将异常抛出
public class Circle {
private double radius;
private static final double PI = 3.14;
// 圆需要构造方法,在创建的时候给半径进行初始化
public Circle( double radius ) {
// 对半径进行判断
if( radius <= 0 ) {
// 手动抛出异常
throw new IllegalArgumentException("圆的半径不能小于等于零");
}
this.radius = radius;
}
// 获取圆面积
public double getArea() {
return this.radius * this.radius * PI;
}
}
4.2、方法调用者捕获异常
public class ExceptionDemo2 {
public static void main(String[] args) {
try {
// 创建圆的对象,但是传递的半径大于零
Circle c = new Circle( -3 );
double area = c.getArea();
System.out.println(area);
}catch( IllegalArgumentException e ) {
//IllegalArgumentException e = new IllegalArgumentException("圆的半径不能小于等于零");
//e.printStackTrace();
// 在项目中异常被抓住之后,需要在catch代码块中通过IO技术,将异常的信息写到文件中(记录异常日志)
// 后期项目的维护人员,通过获取异常日志文件,查阅项目运行过程中的一些问题,进而给出解决方案
}
}
}
5、Java中的异常体系
在Java中已经将程序可能出现的常见的异常现象都已经安装面向对象的思想,将异常封装成不同的类。
如果在开发中遇到了类似的问题,需要抛出异常,这时可以借助Java中相关的异常类名,将这个异常抛出。
在JDK中异常的顶层父类Throwable,它下面有2个子类:
- Error:错误
- Exception:异常
- Exception:Exception或Exception下非RuntimeException的子类都称为编译时期异常,在程序中如果用到别的方法上通过throws关键字显示声明了编译时期的异常,这时需要在自己调用的代码中给出解决方案(1、在方法上使用throws继续声明,2、在方法内部使用try - catch捕获)、
- RuntimeException:运行时期异常,可以在编写代码的时候不管。
6、自定义异常
在项目中会遇到一些异常问题,在JDK找不到一个正确的异常类描述,这时一般需要在项目中自定义一些异常类,然后表示项目中某些特殊的异常信息。
通过观察JDK中已经存在某些异常类,发现从Exception类往下的所有异常类他们中只提供的一些构造方法,没有其他额外方法,在构造方法中都是通过super语句将参数等信息交给父类处理。最终所有异常都是交给Throwable类进行处理。
自定义异常类:
1、定义类,提供相关的构造方法
2、类需要找到JDK中某个异常类作为父类
3、在构造方法中通过super语句将参数交给父类处理
/*
* 如果自定义异常属于编译时期需要处理的,继承Exception,
* 如果属于运行时期的,继承RuntimeException
*/
public class RadiusException extends Exception {
public RadiusException() {
super();
}
public RadiusException(String msg) {
super(msg);
}
}
一般项目中自定义异常的包名: com.neusoft.exce
7、异常的其他组合
关于try-catch的组合还有别的写法:
// try-catch组合捕获异常
try{
可能有异常的代码
}catch( 异常类名 变量名 )
{
处理异常的代码
}
// 可能程序中会出现多种异常, 每个异常都需要单独捕获进行异常的处理
try{
可能有异常的代码
}
catch( 异常类名 变量名 )
{
处理异常的代码
}
catch( 异常类名 变量名 )
{
处理异常的代码
}
catch( 异常类名 变量名 )
{
处理异常的代码
}
// 上面这个多catch捕获异常的时候,一定要从小的异常往大的异常捕获
// try-catch-finally
try{
可能出现异常的代码
}catch( 异常类型 变量名 ){
}
........
finally{
书写的必须要执行的代码
}
public class ExceptionDemo4 {
public static void main(String[] args) {
try {
int s = div(1,0);
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("必须执行的代码");
}
}
// 除法运算
public static int div(int a , int b) {
return a / b;
}
}
// try - finally : 组合异常是没有被捕获的,程序中有某些代码是必须执行的,但不管有没有异常,也不进行异常的处理
try{
}finally{
}
面试题:final(修饰符)、finally(异常中必须要执行代码)、finalize(通知垃圾回收器) 区别?
8、方法复写中异常问题
方法重载:在一个类中方法名相同,参数列表不同(个数、类型、顺序)
方法重写:在子父类之间子类中出现了和父类一模一样的方法(返回值类型、方法名、参数列表一致)。
重写异常的细节:
- 当父类的方法没有声明(throws)异常,子类复写父类的方法时也不能声明异常。
- 当父类的方法声明异常,子类复写的时候可以声明相同的异常或者异常的子异常。
- 当父类的方法声明多个异常(throws 异常1,异常2,异常3),子类复写父类的方法时可以声明这些异常中部分。