Java学习笔记(十三)面向对象---异常

一曲冷凌霜 提交于 2020-02-28 22:50:10

概述

对异常的理解

程序在运行过程中出现不正常情况。是对问题的描述,将问题进行对象的封装。

异常的由来

问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述,并封装成对象。

对于问题的划分

一种是严重的问题,一种是非严重的问题。

  • 对于严重的:Java通过Error类进行描述。
    对于Error一般不编写针对性的代码对其进行处理。
  • 对于非严重的:Java通过Exception类进行描述。
    对于Exception可以使用针对性的处理方式进行处理。
    无论Error还是Exception都具有一些共性内容。
    比如:不正常情况的信息,引发原因。

    异常体系

    Throwable
        |--Error
        |--Exception
            |--RuntimeException

异常体系的特点:
异常体系中的所有类以及被建立的对象都具备可抛性。

异常的处理

代码语句格式

try {
    需要被检测的代码
}
catch(异常类 变量) {
    处理异常的代码(处理方式)
}
finally {
    一定会执行的语句;
}

对异常的常见操作方法

String getMessage();
String toString();
void printStackTrace();

class Demo {
    int div(int x,int y) {
        return x/y;
    }
}
public class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int z = d.div(1,0);
            System.out.println(z);
        }
        catch (Exception e) {  //Exception e = new ArithmeticExceptin();
            System.out.println(e.getMessage()); //  /by zero
            System.out.println(e.toString());   //  异常名称:异常信息
            e.printStackTrace();                //  异常名称:异常信息,异常出现的位置
            //(jvm默认的处理异常的机制就是在调用printStackTrace()方法)
        }
        System.out.println("over");
    }
}

运行结果

/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
    at Demo.div(ExceptionDemo.java:3)
    at ExceptionDemo.main(ExceptionDemo.java:10)
over

异常声明throws

  • throws关键字用来声明一个方法有可能会出现问题。
  • throws关键字可以将异常抛给调用者,可以层层向上抛,直到最后由Java虚拟机抛出。
    比如:
class Demo {
    int div(int x,int y) throws Exception {
        return x/y;
    }
}
public class ExceptionDemo1 {
    public static void main(String[] args) throws Exception {
        Demo d = new Demo();
        int y = d.div(1,0);
        System.out.println(y);
        System.out.println("over");
    }
}
  • 如果调用者对被调用方法抛出的异常不加处理会出现编译错误。
class Demo {
    int div(int x,int y) throws Exception {
        return x/y;
    }
}
public class ExceptionDemo2 {
    public static void main(String[] args) {
        Demo d = new Demo();
        int y = d.div(1,0);
        System.out.println(y);
        System.out.println("over");
    }
}

上述代码编译会提示:

Error:(9, 22) java: 未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出

我们对可能出现的异常加以处理:

class Demo {
    int div(int x,int y) throws Exception {
        return x/y;
    }
}
public class ExceptionDemo3 {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int y = d.div(1,0);
            System.out.println(y);
        }
        catch (Exception e) {
            System.out.println(e.toString());
        }
        System.out.println("over");
    }
}

运行结果:

java.lang.ArithmeticException: / by zero
over

对可能出现异常的div方法进行处理之后,程序可以正常编译运行。

多异常处理

1.声明异常时,声明为更为具体的异常,这样可以处理得更具体。
2.方法声明有几个异常,对应就有几个catch块。(不要定义多余的catch块)
如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。

class Demo {
    int div(int x,int y) throws ArithmeticException,ArrayIndexOutOfBoundsException {
        int[] arr = new int[x];
        System.out.println(arr[3]);
        return x/y;
    }
}
public class ExceptionDemo4 {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int y = d.div(2,0);
        }
        catch (ArithmeticException e) {
            System.out.println("被零除了");
        }
        catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组角标越界");
        }
    }
}

运行结果:

数组角标越界

上述代码中显然有两个一场出现,但是当方法中出现一个异常之后方法会停止运行,不会继续执行下去。

自定义异常

项目中会出现特有的问题,而这些问题并未被Java描述并封装对象。所以对这些特有的问题可以按照Java的对问题封装的思想,将特有的问题,进行自定义的异常封装。

需求:在本程序中,对于除数是负数,也视为是错误的。
当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理。
要么在内部使用try catch处理。
要么在函数上声明让调用者处理。

class FuShuException extends Exception {

}
class Demo {
    int div(int x,int y) throws FuShuException{
        if(y<0) {
            throw new FuShuException();//通过throw关键字手动抛出一个自定义异常对象
        }
        return x/y;
    }
}
public class ExceptionDemo5 {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int y = d.div(1,-1);
            System.out.println(y);
        }
        catch (FuShuException e) {
            System.out.println(e.toString());
            System.out.println("除数出现负数了");
        }
    }
}

运行结果

FuShuException
除数出现负数了

我们可以发现以上的代码运行的结果中只有异常的名称,却没有异常的信息。因为我们并未在自定义的异常中定义具体信息。

定义异常信息

因为父类中已经把异常信息的操作完成了。所以子类只要在构造时,将异常信息传递给父类通过super语句,就可以通过getMessage方法获取自定义的异常信息。

class FuShuException extends Exception {
    private int value;
    FuShuException(){
        super();
    }
    FuShuException(String message,int value) {
        super(message);
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}
class Demo {
    int div(int x,int y) throws FuShuException{
        if(y<0) {
            throw new FuShuException("--被零除了--",y);//通过throw关键字手动抛出一个自定义异常对象
        }
        return x/y;
    }
}
public class ExceptionDemo5 {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int y = d.div(1,-1);
            System.out.println(y);
        }
        catch (FuShuException e) {
            System.out.println(e.toString());
            System.out.println("除数出现了负数:"+e.getValue());
        }
    }
}

运行结果:

FuShuException: --被零除了--
除数出现了负数:-1

注意

自定义异常必须是是自定义类继承Exception
异常体系中的异常类和异常对象都具备可抛性,这个可抛性是Throwable这个体系中独有的特点。只有这个体系中的类和对象才可以被throw和throws操作。

throw和throws的区别

  1. throws使用在函数上,throw使用在函数内。
  2. throws后面跟的是异常类,可以跟多个用逗号隔开。throw后跟的是异常对象。

RuntimeException

异常分两种

  1. 编译时被检测的异常。
  2. 编译时不被检测的异常(运行时的异常。RuntimeException以及其子类)

RuntimeException是Exception中的一个特殊的子类。
如果在函数内容中抛出异常,函数上可以不用声明。
如果在函数上声明了异常,调用者可以不用进行处理。

之所以不用在函数上声明是因为不需要让调用者处理,当该异常发生,程序希望停止,对代码进行修正,而不是去让调用者处理。

自定义异常时,如果该异常发生,无法再继续进行运算,就让自定义异常继承RuntimeException。

class FuShuException extends RuntimeException{
    FuShuException(String message) {
        super(message);
    }
}
class Demo {
        int div(int x,int y) {  //函数上为声明异常也可以正常编译
            if (y < 0) {
                throw new FuShuException("--除数为负数--");//在函数内容中抛出异常
            }
            else if(y == 0){
                throw new ArithmeticException("--除数为零--");//在函数内容中抛出异常
            }
            return x/y;
        }
}
public class ExceptionDemo6 {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int y = d.div(1,-1);
        }
        catch (FuShuException e) {
            System.out.println(e.toString());
        }
        catch (ArithmeticException e) {
            System.out.println(e.toString());
        }
    }
}

finally

try {

}
catch () {

}
finally {

}

finally中存放的是一定会被执行(无论是否有异常)的代码,即使catch中有return语句。

finally只有一种情况不会被执行,当代码执行到System.exit(0);

例如:如果需要对数据库进行操作,无论有没有成功连接上数据库,在最后操作完成的时候,都需要断开数据库的连接,以释放有限的资源。

异常处理语句的另一种格式

try {
    
}
finally {

}

异常在覆盖时的特点

  1. 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法只能抛出父类的异常或者该异常的子类。子类程序在继承时不能抛出新的异常。
  2. 如果父类方法抛出多个异常,那么子类在覆盖方法时,只能抛出父类异常的子集。
  3. 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常如果子类方法发生了异常,就必须要进行try处理,不可以抛出。

异常的好处

  1. 将问题进行封装。
  2. 将正常流程代码和问题处理代码相分离,增强代码可读性。

    异常的处理原则

  3. 两种处理方式: try和throws
  4. 调用到抛出异常的功能时,抛出几个,就需要处理几个。一个try对应多个catch
  5. 多个catch需要将父类异常的catch放到最下面。
  6. catch内,需要定义针对性处理方式,不要简单地定义printStackTrace。
    • 当捕获到地异常,无法处理,可以继续在catch中抛出该无法处理的异常
    • 如果该异常处理不了,但并不属于该功能出现的异常,可以将该异常进行转换然后再抛出
    • 如果异常可以处理,但需要使抛出的异常和本功能相关,也可以对异常进行转换

异常练习

有圆形和长方形,都可以获取面积,对于面积,如果出现非法的数值,视为是获取面积出现问题。
出现的问题通过异常来表示。(使用异常可以,使问题处理代码和正常流程代码分开)

interface Shape {
    void getArea();
}
class InvaidValueException extends RuntimeException {//如果一个传入一个图形的参数有错误,我们无法对其进行有效的处理,只抛出异常即可,所以继承RuntimeException
    InvaidValueException(String message) {
        super(message);
    }
}
class Rec implements Shape {
    private double len,wid;
    Rec(double len,double wid) {
        if(len <= 0 || wid <= 0)
                throw new InvaidValueException("非法值");
        this.len = len;
        this.wid = wid;
    }
    @Override
    public void getArea() {
        System.out.println(wid*len);
    }
}

class Circle implements Shape {
    private double r;
    public static final double PI = 3.14;
    Circle(double r)  {
        if(r <= 0)
            throw new InvaidValueException("非法值");
        this.r = r;
    }
    @Override
    public void getArea() {
        System.out.println(r*r*PI);
    }
}
public class ExceptionDemo7 {
    public static void main(String[] args) {
        Rec R = new Rec(1,2);
        R.getArea();
        Circle C = new Circle(-1);
        C.getArea();
        System.out.println("over");
    }
}

上述程序的运行结果为:

2.0
Exception in thread "main" InvaidValueException: 非法值
    at Circle.<init>(ExceptionDemo7.java:28)
    at ExceptionDemo7.main(ExceptionDemo7.java:41)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!