DUFQ_2020.12.16-杜飞强-异常处理

方法可变参数

方法可变参数是指方法形参的个数可以变化,个数可以为0,或者n个。

定义方法可变参数

参数使用...定义方法可变参数,eg:

public double area(int ...param){
    
}

方法可变参数的规则

规则1:一个方法只能有一个可变参数

错误示例

public void area(int a,int b, int ...c) {
    
}
规则2:如果一个方法有多个参数,那么可变参数必须定义在最后
public void area(int a,int b, int ...c) {
    
}
规则3:方法可变参数本质上是一个数组,因此可变参数可以被当做数组来使用
public void area(int ...c) {
    if(c!=null && c.length>0) {
        for (int i = 0; i < c.length; i++) {
            System.out.println(c[i]);
        }
    }
}

方法可变参数的调用

调用方法1:不传入参数

调用方法2:传入多个离散值

调用方法3:传入数组对象

eg:

Public static void area(int ...c) {
    if(c!=null && c.length>0) {
        for (int i = 0; i < c.length; i++) {
            System.out.println(c[i]);
        }
    }
}
public static void main(String[] args) {
    area();//无参调用,此时area()方法的参数c是空指针
    area(1,2,3,4,5);//传入多个离散值,此时area()方法参数c是一个数组,传入的参数是数组元素。
    int score[] = {67,78,56};
    area(score);// 传入数组对象,此时area()方法参数c是一个数组。
}

异常

定义

异常是指程序在编译时,或者是运行时发生不寻常的事情。

发生异常后的处理

发生异常要捕获,如果一个程序有异常发生,那么程序就无法正常运行了,导致程序强行终止运行。当程序发生异常时,应该捕获异常,只要异常被捕获,则程序就没有异常了,所以程序就能够继续运行了。即捕获异常是为了保证程序能够继续运行。

异常的体系结构

Throwable类

Throwable类是异常体系结构的父类。Throwable类的父类是Object,在Throwable下分为两种预处理方法,分别是Error和Exception,其中,Error是指错误,Exception是指异常。

Error类

Error是指无法通过开发人员编写程序能够解决的异常。

典型的Error:内存溢出、动态链接失败(加载dll文件)、虚拟机错误

Exception类

Excepton是所有异常类的父类,这里所说的异常是指能够通过编写程序处理的异常。Exception类有两大子类,一个是运行时异常,一个是编译时异常。

规定:编译时异常必须捕获,如果不捕获会导致程序无法编译,即无法运行。eg:ClassnotfoundException就是编译时异常

异常的解决方式:try,catch解决

RuntimeException类

RuntimeException类是所有运行时异常的父类。

捕获异常

java中有五个与捕获异常相关的关键字,分别是try ,catch,finally,throw,throws。这五个异常关键字分别分成两类处理异常的方式,其中try,catch,finally一组专门处理异常的方式,throw,throws一组专门处理异常的方式。

try,catch,finally异常处理

用法1:

try {
            
} catch (Exception e) {
            
}

用法2:

try {
    
}catch (Exception e) {
    
}finally {
    
}

用法3:

try {
    
} finally{
    
}

解析:

1.try中用于包裹住可能发生异常的代码。

2.catch中用于捕获异常,并对异常进行处理。

3.finally中用于回收资源

4.try与catch组合时,一个try可以与多个catch组合,但是try只能有一个。

5.try与finally组合时,一个try只能与一个finally组合,有且只有一个。

6.try不能单独使用,catch不能单独使用,finally不能单独使用,catch和finally不能组合使用。

异常捕获情况

不捕获异常时
public class Demo1 {
    public static void main(String[] args) {
        int i=1,j=0,res;
        System.out.println("begin");
        res = i/j;
        System.out.println("end");
    }
}

运行结果:

begin
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at homework01.Demo1.main(Demo1.java:6)

分析:

输出begin,没有输出end,说明程序发生异常,,没有捕获,导致系统强制终止运行,提示异常信息为算术异常,除零异常。

使用try和catch捕获异常
public static void main(String[] args) {
        try {
            int i = 1, j = 0, res;
            System.out.println("begin");
            res = i / j;//抛出异常
            System.out.println("end");
        } catch (Exception e) {//捕获异常
            System.out.println("catched");
            e.printStackTrace();
        }
        System.out.println("over");
    }

运行结果

begin

catched

java.lang.ArithmeticException: / by zero

at homework01.Test.main(Test.java:7)

over

分析

1.try中放了可能发生异常的代码

2.如果try中发生了异常,那么程序就会立即被catch捕获,异常捕获后异常就没有了,程序就可以继续运行。

3.异常发生后,程序进入到catch,导致从异常发生位置开始到catch位置之间的代码不再运行

4.当try中没有发生异常时,程序不会进入到catch中执行

finally
public static void main(String[] args) {
    try {
        int i = 1, j = 0, res;
        System.out.println("begin");
        res = i / j;
        System.out.println("end");
    } catch (ArithmeticException e) {
        System.out.println("catched");
        e.printStackTrace();
    } finally {
        System.out.println("finally");
    }
    System.out.println("over");
}

运行结果

begin

catched

java.lang.ArithmeticException: / by zero

at homework01.Test.main(Test.java:7)

finally

over

分析

1.当try中有finally时,finally必须执行

2.如果没有发生异常,执行顺序就是try-finally

3.如果发生了异常,执行顺序是try-catch-finally

4.当try中包含return时,执行顺序是try-catch-finally-return

不管程序是否会发生异常,finally都会执行,只有当遇到System.exit(0);时才不会执行

多重catch
public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.println("计算开始");
            int i, j, res;
            System.out.println("请输入被除数");
            i = input.nextInt();
            System.out.println("请输入除数");
            j = input.nextInt();
            res = i / j;
            System.out.println(i + "/" + j + "=" + res);
            System.out.println("计算结束");
        } catch (InputMismatchException e) {
            System.out.println("除数和被除数必须都是整数");
        } catch (ArithmeticException e) {
            System.out.println("除数不能为零");
        } catch (Exception e) {
            System.out.println("其他异常" + e.getMessage());
        } finally {
            System.out.println("感谢使用本程序");
        }
        System.out.println("程序结束");
    }

分析

一个try可以配置多个catch,但是多个catch是有先后顺序的,必须把子类的catch卸载前面,把父类的catch写在后面,并列的catch没有先后顺序。

throw,throws异常处理

Throws

用于定义在方法声明的后面,表示该方法可能会抛出异常,并告诉主调方法需要处理该方法抛出的异常

Throws不能单独写成一行代码,后面是抛出的异常,可抛出多个异常类型,多个之间用逗号分隔

Throw

Throw用在方法内部,后面紧跟一个异常对象,表示要抛出这个异常

用法1:被调方法不处理异常,直接抛给主调方法
public static void main(String[] args) {
        try {
            divide();
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }
    
    public static void divide() throws ArithmeticException{
        System.out.println("start");
        int i=1,j=0;
        int res = i/j;
        System.out.println("over");
    }

运行结果

运行结果

start

java.lang.ArithmeticException: / by zero

at homework01.Test.divide(Test.java:19)

at homework01.Test.main(Test.java:10)

分析

1.main方法是主调方法

2.divide是被调方法,被调方法内部发生异常时,被调方法没有处理异常,而是将异常抛给主调方法,由主调方法处理异常

用法2:被调方法抓住异常后再次抛出该主调方法(常用)

public static void main(String[] args) {
        try {
            divide();
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    }
    
    public static void divide() throws ArithmeticException{
        try {
            System.out.println("start");
            int i=1,j=0;
            int res = i/j;
            System.out.println("over");
        } catch (Exception e) {
            ArithmeticException ex = new ArithmeticException("除零异常");
            throw ex;
        }
    }
运行结果

start

java.lang.ArithmeticException: 除零异常

at homework01.Test.divide(Test.java:23)

at homework01.Test.main(Test.java:10)

分析

1.被调方法先自己处理异常,在抛出异常该主调方法

2.int res = i/j,由于j=0,JVM抛出ArithmeticException异常,该异常被catch抓住,所有catch中的e就是i/j时抛出的异常,但是这个异常是由于JVM创建并抛出的,因此是英文提示

3.改为中文提示,方法是开发人员自定义一个异常对象,在异常对象的构造函数中输入中文异常描述信息,再将这个异常对象使用throw关键字抛出

4.在主调方法中捕获异常就是中文信息

自定义异常

java关于异常提供了很多api,如果浙西异常api不能满足需要,则定义异常类,满足业务需求

自定义步骤
第一步:定义异常类,继承Exception或者RuntimeException

继承Exception表示自定义编译时异常

继承RuntimeException表示自定义运行时异常

第二步:设置异常的描述信息

在Throwable类中定义了描述异常信息的属性,名字是message。可以将自定义异常的描述存储在该属性中。通过构造函数可将异常描述信息传递到throwable中赋值给message属性

自定义示例

为学生实体对象性别赋值时,只要不是男或者女就是错误的,需要抛出性别异常。

先自定义一个性别异常,命名为GenderException

class GenderException extends RuntimeException{
    public  GenderException(String message){
        super(message);
    }
}

class Student{
    private String gender;

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        if("男".equals(gender) ||"女".equals(gender)) {
            this.gender = gender;
        }
        else {
            throw new GenderException("性别错误,必须是男或女");//抛出异常
        }
    }
}


public class Test {
    public static void main(String[] args) {
        try {
            Student s =new Student();
            s.setGender("公");
        } catch (GenderException e) {
            String message = e.getMessage();
            System.out.println(message);
            e.printStackTrace();
        }
    }
}
异常相关的方法

getMessage():获取异常描述的信息

printStackTrace():输出异常的堆栈信息

评论