异常

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+"不合法!正确的年龄范围是:0150之间!");

}

}

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. throwthrows的区别

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. trycatchfinally允许的组合

可以的组合:

必须有try代码块;

三个代码块都不能单独存在;

try可以和其它两个单独组合,也可以三个一起使用;

catch可以多次出现;

因为没有catch捕获异常,所以相当于没有处理异常,所以还要对异常进行声明,否则回编译报错!

 

4.4. 继承中方法重写的异常

子类重写父类函数,不能比父类函数声明更多编译期异常;

4.4.1. 父类方法没有声明异常,子类重写的方法不能声明编译时异常

4.4.2. 父类方法声明了一个编译异常,子类重写的方法只能声明这个异常或它的子异常

即子类可不声明,可为父类异常本身,可为父类异常的子类,但不可为父类异常的父类,不可为父类异常的同级异常

4.4.3.父类方法声明了多个编译异常,子类重写的方法只能声明这些异常的子集或子集的子类

子类重写父类函数,不能比父类函数声明更多编译期异常;

子类重写父类函数,不能比父类函数声明更多编译期异常;

子类重写父类函数,不能比父类函数声明更多编译期异常;

5. 异常总结

异常的概念:

异常,就是不正常的情况;

程序中的异常,就是程序出现期望之外的不正常情况;

异常的作用:

异常在程序运行中不可避免,合理设置异常和处理代码,可以提高程序的安全型和健壮性;

异常的体系:

java中异常也是一种特殊的事物,需要使用类来描述,描述异常信息的类就是异常类;

java中所有异常类都属于一个继承体系,就是java的异常体系:

Throwablejava异常体系的最高父类;java中所有可被抛出、捕获或声明的异常类,都要继承这个类或它的子类;

|----Error:错误,表示程序中严重的不能被JVM处理的问题;遇到这种问题,JVM都会停止运行;

|----Exception:异常,表示程序中可以被JVM处理的问题;如果程序中抛出该类或它的子类的异常,编译时就会检查有没有书写处理的代码;如果没有写,就会报错;

|----RuntimeException:表示运行时异常;该类和它的子类表示的异常,在编译期不会检查;

异常的分类:

编译期异常:在编译期会被检测有没有处理代码的异常;

运行时异常:在编译时不会被检测的异常;

自定义异常的方法:

1、定义一个类,让它继承ExceptionRuntimeException

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("索引不存在");

}

}

}

原文地址:https://www.cnblogs.com/dongfangshenhua/p/6800013.html