异常概述
异常就是程序运行过程中出现了不正常现象导致程序中断
在Java中, 把各种异常现象进行了抽象形成了异常类
演示受检异常(编译时异常)
public static void main(String[] args) {
//在程序中读取文件的内容, 可以使用FileInputStream类
//通过构造方法,指定要读取的文件
// FileInputStream fis = new FileInputStream("d:/abc.txt");
/*
* 系统定义FileInputStream构造方法时, 通过throws声明抛出了一个FileNotFoundException异常
* 在定义方法时, 通过throws声明抛出的异常就是受检异常
* 在调用方法时, 必须对受检异常进行预处理
* 预处理就是在程序运行时,如果出现了该异常应该怎么办, 就像在楼道中必须有灭火器和消防栓一样
*/
}
演示运行时异常
public static void main(String[] args) {
divide(10, 2);
divide(10, 0);
System.out.println("main..end..");
}
//定义方法, 计算两个整数的商
public static void divide( int x, int y) {
int result = x / y; //整数相除, 除数为0会产生java.lang.ArithmeticException异常, 导致了程序中断
System.out.println( x + "/" + y + "=" + result);
}
异常处理
运行时异常不需要预处理,通过规范的代码可以避免
受检异常必须预处理, 否则编译报错, 有两种预处理方式 :
1)捕获处理
2)抛出处理
异常捕获处理
try{
对可能产生异常的代码进行检视
如果try代码块的某条语句产生了异常, 就立即跳转到catch子句执行,try代码块后面的代码不再执行
try代码块可能会有多个受检异常需要预处理, 可以通过多个catch子句分别捕获
}catch( 异常类型1 e1){
捕获异常类型1的异常,进行处理
在开发阶段, 一般的处理方式要么获得异常信息,要么打印异常栈跟踪信息
e1.printStackTrace();
在部署后, 如果有异常,一般把异常信息打印到日志文件中
} catch( 异常类型2 e1){
捕获异常类型2的异常,进行处理
如果捕获的异常类型有继承关系, 应该先捕获子异常再捕获父异常; 如果没有继承关系, catch子句没有先后顺序
}finally{
不管是否产生了异常,finally子句总是会执行
一般情况下,会在finally子句中释放系统资源
}
对异常进行处理后, 在程序运行过程中,即使出现了异常,程序也不会中断,继续向下执行, 这就是异常预处理的作用,异常预处理可以提高程序的健壮性(鲁棒性,Robust)
try/catch/finally子句都不能单独使用, try…catch…/try…finally/ try…catch…finally,finally子句总是会执行
注意:final/finalize/finally三者只是名字相像,并没有任何区别
关于finally演示
public static void main(String[] args) {
int result = m1(10);
System.out.println( result ); //10
result = m2(10);
System.out.println( result); //11
}
private static int m2(int i) {
try {
return i;
} finally {
i++;
return i;
}
}
public static int m1( int xx) {
try {
return xx;
} finally {
xx++;
}
/*
* 程序执行return xx时, 并没有立即执行return语句, 而是把xx的值保存到临时变量中
* 执行finally子句, xx加1
* 再执行return语句, 把临时变量的值返回
*/
}
throws抛出处理
在定义方法时, 如果方法体中有 受检异常需要预处理, 可以捕获处理,也可以抛出处理.
谁调用这个方法,谁负责处理该 异常,
在定义方法时,把异常抛出就是为了提醒方法的使用者,有异常需要预处理
在处理异常时, 是选择捕获处理还是抛出处理??
1)一般情况下, 在调用其他方法时, 如果被调用的方法有受检异常需要预处理, 选择捕获处理
2)在定义方法时, 如果方法体中有受检异常需要预处理,可以选择捕获 ,也可以选择抛出处理; 如果方法体中通过throw语句抛出了一个异常对象,所在的方法应该使用throws声明该异常
main方法抛出的异常JVM负责处理, JVM的处理方式就中断程序, 打印异常信息
方法覆盖中的异常处理
方法覆盖(重写)规则:
1)方法签名必须相同, 方法名与参数列表就是方法签名
2)方法的返回值类型可以相同 , 子类方法的返回值类型可以是父类方法返回值类型的子类型
3)子类方法的访问权限可以更宽泛(更大),
- 如果父类方法使用public修饰,子类方法只能是public修饰
- 如果父类方法使用protected修饰,子类方法可以是protected/public修饰
4)子类方法的异常要比父类方法的异常更小,
- 如果父类方法没有抛出异常,子类重写后也不能抛出异常
- 如果父类方法抛出了异常,子类方法可以抛出相同的异常,也可以抛出父类异常的子异常,也可以不抛出异常
异常在开发中的应用
自定义异常
1)定义一个类继承Exception
2)在自定义异常类中提供两个构造方法,一个是无参的,一个是带有一个String形参的
3)在需要的位置通过throw抛出异常对象
4)throw所在的方法通过throws声明该异常
5)调用方法时,需要对受检异常预处理