1. 异常介绍
1.1. 什么是异常
异常,就是不正常情况;
生活中的异常:感冒了;房子漏水;王宝强老婆出事;电脑蓝屏;骑车掉链子;过马路被狗咬了……;
java中的异常:空指针异常;数组角标越界异常;类型转换异常……
程序中出现不符合预期的情况就是异常;
1.2. 异常的作用
需求:计算两个整数的商;
1、程序中不可能保证没有问题;即使我们的逻辑很严谨,但是程序一般是要给用户使用的,我们是不能控制用户的输入的;而且有的程序会需要使用第三方的数据,而这第三方的数据也不一定能保证没有问题;所以我们的程序要避免出现问题,是不可能的;
一个好的程序,不是不会出现问题,而是出现问题后,有预备方案可以解决;异常可以提高程序健壮性;
2、程序中真的出现问题了,需要有一种机制可以把错误发生的位置、原因等信息记录并显示出来,方便开发者排查错误;
研究异常,就是为了解决上面提出来的问题;
1.3. java中异常的发生过程
2. 自定义异常
问题:JDK自己提供的异常很全面,根据开发中大多数问题,都有相应的异常类,但是我们使用起来是比较麻烦的;
1、异常类太长,记忆和书写都不方便;
2、异常类很多,开发者不可能都记下来,使用时就很不方便;
2.1. 自定义异常
需求:创建一个描述人的信息的类,需要对年龄的合法性进行判断;
//需求:创建一个描述人的信息的类,需要对年龄的合法性进行判断;
class Person//表示人的信息
{
private String name;//姓名
private int age;//年龄
private String sex;//性别
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
if(age > 0 && age < 150){
this.age = age;
}else{
System.out.println("年龄"+age+"不合法!正确的年龄范围是:0到150之间!");
}
}
public int getAge(){
return age;
}
public void setSex(String sex){
this.sex = sex;
}
public String getSex(){
return sex;
}
public Person(String name,int age,String sex){
this.name = name;
setAge(age);//调用函数进行赋值,可以在这个函数里进行数据有效性检查
this.sex = sex;
}
//重写toString函数,为了可以直接输出对象的各个属性值
public String toString(){
return "姓名:"+name+";年龄:"+age+";性别:"+sex;
}
}
class Test
{
public static void main(String[] args)
{
Person p = new Person("老王",389,"不详");
System.out.println(p);
}
}
问题:虽然判断出来参数错误了,但是程序仍然可以继续执行,创建了对象,并且给年龄赋了一个不合法的值(默认值);
实际开发中,如果出现错误数据,是一种异常情况,不应该让程序继续执行下去;
所以我们可以考虑使用异常来解决;
结论:在Java中,只有Throwable和它的子类才能作为异常类,对象才能被throw关键字抛出;
结论:Error表示的是严重的程序自己不应该处理的问题,所以我们自定义的异常不应该继承这个类;
结论:Exception和它的子类表示的是合理的程序应该自己处理的问题;所以我们自定义异常,应该继承这个类;
结论:RuntimeException和它的子类表示的异常,可以不需要声明和捕获;
所以如果一个异常类不是必须要声明或捕获,就可以继承RuntimeException类;
总结自定义异常的一般步骤:
1、要创建一个类,继承RuntimeException或者Exception类;
2、在这个类中提供一个有参构造函数,接收错误信息;
3、在这个有参构造函数中将接收到的错误数据通过super语句传递给父类的构造函数;
2.2. 异常的体系和分类
java中异常的体系:
异常的分类:
编译期异常:
指的是Exception及其子类(不包括RuntimeException和它的子类)表示的异常;
特点是:如果在程序中手动抛出这类异常,编译时编译器会检查有没有书写处理代码,如果没有就会报错;
(也就是说,编译期异常必须处理)
运行期异常:
指的是RuntimeException和它的子类表示的异常;
特点是:如果程序中手动抛出这类异常,不管书写处理代码没有,编译器都不管;
编译期异常和运行期异常,指的是编译的时候,是否检查书写了处理代码;
不管是什么异常,真正发生,都是在程序运行的时候;
如果开发中,有的问题必须要被处理,就应该声明为编译期异常;否则可以声明为运行期异常;
3. 异常的处理
现实生活中,有的时候出了问题,可以自己解决,比如玩电脑,突然蓝屏;就可以字节解决,比如重启电脑;
有的时候,出了问题自己不能解决;比如:走路被车撞断腿了,就不能自己解决;
3.1. 异常的声明
如果程序不能自己解决出现的问题,就需要使用throws关键字,将异常的类型声明出去,告诉调用者,这个函数可能会出现问题;
如果程序中真的出现问题,这个函数就会中断,然后回到调用者那里;
一个函数声明多个异常:
如果函数声明的多个异常之间存在继承关系:
3.2. 异常的捕获
如果程序中出现的问题可以自己解决,就可以使用try-catch代码块,将可能出错的代码包起来,然后书写处理代码;
如果程序中真的出现问题,就可以调用处理代码自己解决,函数的调用者是不知道出问题了的;
这种方法可以捕获多个异常,并且可以针对每个异常作出不同的处理;
如果要捕获多个异常,而且处理办法都一样,就可以使用另一种写法:
使用这种方式需要注意:要捕获的多个异常,不能有继承关系;
一般情况下,如果是因为错误的参数造成的异常,都要声明异常;
如果是因为自己的逻辑问题造成的异常,一般要捕获;
4. 异常的细节
4.1. throw和throws的区别
throw: 用来在函数里面抛出异常对象;
throws: 用来声明函数会抛出异常;
4.2. finally代码块
一般在捕获异常时,在try-catch代码块后面还可以添加一个finally代码块,作用是,在finally块中的代码,一定会被执行;
问题:下列程序的结果是什么?为什么?
class FinallyDemo
{
public static void show(int a){
try{
if(a == 1){
System.out.println("11111");
throw new Exception();
}else{
System.out.println("22222");
}
System.out.println("333333333333");
}catch(Exception e){
System.out.println("exception");
return ;
}finally{
System.out.println("finally");
}
}
public static void main(String[] args)
{
show(1);
System.out.println("-----------------------------------------");
show(2);
}
}
结论:不管程序中会不会出现异常,finally代码块中的代码都要执行到!
4.3. try、catch、finally允许的组合
可以的组合:
必须有try代码块;
三个代码块都不能单独存在;
try可以和其它两个单独组合,也可以三个一起使用;
catch可以多次出现;
因为没有catch捕获异常,所以相当于没有处理异常,所以还要对异常进行声明,否则回编译报错!
4.4. 继承中方法重写的异常
子类重写父类函数,不能比父类函数声明更多编译期异常;
4.4.1. 父类方法没有声明异常,子类重写的方法不能声明编译时异常
4.4.2. 父类方法声明了一个编译异常,子类重写的方法只能声明这个异常或它的子异常
即子类可不声明,可为父类异常本身,可为父类异常的子类,但不可为父类异常的父类,不可为父类异常的同级异常
4.4.3.父类方法声明了多个编译异常,子类重写的方法只能声明这些异常的子集或子集的子类
子类重写父类函数,不能比父类函数声明更多编译期异常;
子类重写父类函数,不能比父类函数声明更多编译期异常;
子类重写父类函数,不能比父类函数声明更多编译期异常;
5. 异常总结
异常的概念:
异常,就是不正常的情况;
程序中的异常,就是程序出现期望之外的不正常情况;
异常的作用:
异常在程序运行中不可避免,合理设置异常和处理代码,可以提高程序的安全型和健壮性;
异常的体系:
java中异常也是一种特殊的事物,需要使用类来描述,描述异常信息的类就是异常类;
java中所有异常类都属于一个继承体系,就是java的异常体系:
Throwable:java异常体系的最高父类;java中所有可被抛出、捕获或声明的异常类,都要继承这个类或它的子类;
|----:Error:错误,表示程序中严重的不能被JVM处理的问题;遇到这种问题,JVM都会停止运行;
|----:Exception:异常,表示程序中可以被JVM处理的问题;如果程序中抛出该类或它的子类的异常,编译时就会检查有没有书写处理的代码;如果没有写,就会报错;
|----:RuntimeException:表示运行时异常;该类和它的子类表示的异常,在编译期不会检查;
异常的分类:
编译期异常:在编译期会被检测有没有处理代码的异常;
运行时异常:在编译时不会被检测的异常;
自定义异常的方法:
1、定义一个类,让它继承Exception或RuntimeException;
2、创建一个构造函数,接收一个表示异常信息的参数;
3、调用父类的有参构造函数,将接收的异常信息传递给父类的构造函数;
异常的处理:
声明:
使用关键字:throws,在函数的参数列表后面,大括号前面书写函数可能会抛出的异常;多个异常用逗号分开;
捕获:
使用try-catch块包括可能抛出异常的代码。并在catch后面的括号中定义接收抛出的异常对象,在catch后面的大括号里面书写处理代码;
public class ExceptionDemo2 {
public static void main(String[] args) {
// method1();
// method2();
// method3();
// method4();
// method5();
}
//一个异常
//当没有处理异常时,若出现异常,异常之后的代码就不再执行
//当处理异常时,若出现异常,会执行catch中的代码,并继续走程序
private static void method1() {
int a=10;
int b=0;
try {
System.out.println(a/b);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
}
System.out.println("over");
}
//两个异常(知识一)
//一个 try 一个 catch
//两个异常的代码都可以执行
private static void method2() {
int a=10;
int b=0;
try {
System.out.println(a/b);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
}
int[] arr={1,2,3,4};
try {
System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("索引不存在");
}
}
//两个异常(知识二)
//一个 try 多个 catch
//try中代码中,按顺序从上到下执行,遇到异常找到
//相应的catch就直接结束try catch,后面的异常不再执行,但程序还会继续往下走
//多个catch的顺序无所谓
private static void method3() {
int a=10;
int b=0;
int[] arr={1,2,3,4};
try {
System.out.println(arr[5]);
System.out.println(a/b);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("索引不存在");
}
System.out.println("end");
}
//两个异常(知识三)
//一个 try 多个 catch
//try中的异常找catch异常时,是按从小到大依次寻找的,若最后还找不到则报错
private static void method4() {
int a=10;
int b=1;
int[] arr={1,2,3,4};
try {
System.out.println(a/b);
System.out.println(arr[5]);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
// } catch (ArrayIndexOutOfBoundsException e) {
// System.out.println("索引不存在");
}catch (Exception e) {
System.out.println("大异常");
}
}
//两个异常(知识四)
//平级的异常谁前谁后无所谓,但如果出现了父子关系,父类异常必须放在后面
private static void method5() {
int a=10;
int b=1;
int[] arr={1,2,3,4};
try {
System.out.println(a/b);
System.out.println(arr[5]);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
// }catch (Exception e) {
// System.out.println("大异常");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("索引不存在");
}
}
}