java 异常

异常的概述

异常就是程序在运行时出现的不正常情况。我们不仅要问异常是怎么来的呢?问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述,并封装成对象。异常其实就是Java对不正常情况进行描述后的对象体现。

异常的体系

这里写图片描述 
对于问题的划分有两种:一种是严重的问题,一种是非严重的问题。

  • 对于严重的,Java通过Error类进行描述(通常出现重大问题,如运行的类不存在或者内存溢出等)。对于Error一般不编写针对性的代码对其进行处理。Error是由系统底层发生的,它将告诉JVM,JVM告诉使用者。Error一旦出现不做针对性的处理,直接修改代码。
  • 对于非严重的,Java通过Exception类进行描述。Exception是由JVM发生的,并告诉调用者,对于Exception可以使用针对性的处理方式进行处理。

无论Error或者Exception都具有一些共性内容,比如不正常情况的信息、引发原因等。

异常的处理

Java提供了两种对异常的处理方式:

  1. 遇到问题不进行具体的处理,而是继续抛给调用者。其实就是在函数上通过throws关键字声明异常,告诉调用者处理。对这种处理方式的详细解释:在编写功能时,编写者会知道该功能有可能发生问题,而这个问题很容易来自于调用者传递的参数,而导致功能无法运行。这时发生的问题就应该让调用者知道,并最后让调用者有预先的处理方式,所以在定义功能时,需要在功能上对有可能发生的问题进行声明。声明问题需要使用关键字(throws):throws 异常类,声明的目的:就是让调用者可以进行处理。
  2. 针对性的处理方式——捕获!

    try 
    {
        // 有可能发生异常的代码
    }
    catch(异常类 变量)
    {
        // 这是真正的捕获,处理异常的代码;
    }
    finally
    {
        // 一定会被执行的代码;
    }
    • 异常的常见方法

方法声明功能描述
getMessage()获取异常信息,返回字符串
toString()获取异常类名和异常信息,返回字符串
printStackTrace()获取异常类名和异常信息,以及异常出现在程序中的位置,返回值为void。JVM默认处理收到的异常就是调用这个方法,将信息显示在屏幕上
printStackTrace(PrintStream s)通常用该方法将异常内容保存在日志文件中,以便查阅
class Demo {
    int div(int a, int b) throws Exception { // 在功能上通过throws的关键字声明了该功能有可能会出现问题
        return a/b; // new ArithmeticException();
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int x = d.div(4, 1); // new ArithmeticException();
            System.out.println("x="+x);
        } catch(Exception e) { // Exception e = new ArithmeticException();
            System.out.println("除零啦!");
            System.out.println(e.getMessage()); // / by zero
            System.out.println(e.toString());// 异常名称:异常信息
            e.printStackTrace(); // 异常名称,异常信息,异常出现的位置
            // 其实JVM默认的异常处理机制,就是在调用printStackTrace(),打印异常在堆栈中的跟踪信息
        }
        System.out.println("over");
    }
}
  •  

在函数上声明异常(throws)。便于提高安全性,让调用者进行处理,不处理编译失败。

自定义异常

因为项目中会出现特有的问题,而这些问题并未被Java所描述并封装成对象。所以对于这些特有的问题可以按照Java的对问题封装的思想,将特有的问题,进行自定义的异常封装。那么自定义异常如何定义异常信息呢?因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类,通过super语句,那么就可以直接通过getMessage()获取自定义的异常信息。 
下面通过一个案例的讲解,你就能知道该如何自定义异常了。

/*
需求:在本程序中,对于除数是-1,也视为是错误的,是无法进行运算的,那么就需要对这个问题进行自定义的描述
*/
class FuShuException extends Exception {
    private int value;
    FuShuException() {
        super();
    }
    FuShuException(String msg, int value) {
        super(msg);
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}
class Demo {
    int div(int a, int b) throws FuShuException {
        if(b < 0)
            throw new FuShuException("出现除数是负数的情况----- / by fushu", b); // 手动通过throw关键字抛出一个自定义异常对象
        return a/b;
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int x = d.div(4, -9);
            System.out.println("x="+x);
        } catch(FuShuException e) {
            System.out.println(e.toString());
            // System.out.println("除数出现负数了");
            System.out.println("错误的负数是:"+e.getValue());
        }
        System.out.println("over");
    }
}

由以上程序代码可知,我们需要注意两点:

  1. 自定义异常必须是自定义类继承Exception。继承Exception的原因:异常体系有一个特点,因为异常类和异常对象都可被抛出,它们都具备可抛性,这个可抛性是Throwable这个体系中独有的特点,只有这个体系中的类和对象才可以被throws和throw操作。
  2. 当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。要么在内部try catch处理,要么在函数上声明让调用者处理。一般情况下,函数内出现异常,函数上需要声明。

throw和throws的区别

  • 位置不同

    • throws使用在函数上,throws后面跟的是异常类,可以跟多个,用逗号隔开;
    • throw使用在函数内,throw后跟的是异常对象。
  • 功能不同

    • throws用来声明异常,让调用者知道该功能有可能出现的问题,并由调用者可以给出预先的处理方式;
    • throw抛出具体的问题对象,执行到throw,功能就已经结束了,跳转到调用者,并将具体的问题对象也抛给调用者。

异常的分类

对于异常可分为两种:

  • 编译时被检测的异常;
  • 编译时不被检测的异常(运行时异常,RuntimeException以及其子类)。

编译时被检测异常

只要是Exception和其子类都是,除了特殊子类RuntimeException体系。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式,这样的问题都可以针对性的处理。

编译时不被检测异常(运行时异常)

Exception中有一个特殊的子类异常RuntimeException(运行时异常)。

  1. 如果在函数内容里抛出该异常,函数上可以不用声明,编译一样通过。之所以不用在函数上声明,是因为不需要让调用者处理,当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,程序员对代码进行修正。
  2. 如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。

所以,在自定义异常时,如果该异常的发生,无法再继续进行运算,那么就可让自定义异常继承RuntimeException。例如,

class FuShuException extends RuntimeException {
    FuShuException(String msg) {
        super(msg);
    }
}

class Demo {
    int div(int a, int b) {
        if(b < 0)
            throw new FuShuException("出现了除数为负数啦!");
        if(b == 0) 
            throw new ArithmeticException("被零除啦!");
        return a/b;
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        int x = d.div(4, -9);
        System.out.println("x="+x);
        System.out.println("over");
    }
}

异常处理的原则

  1. 函数内容中如果抛出需要检测的异常,那么函数上必须要声明。否则,必须在函数内用try/catch捕捉,否则编译失败;
  2. 如果调用到了声明异常的函数,要么try/catch,要么throws,否则编译失败;
  3. 什么时候try/catch,什么时候throws呢? 
    功能内部可以解决,用try/catch。解决不了或者解决了还必须告诉调用者问题,用throws告诉调用者,由调用者解决。
  4. 一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性处理。内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。

对多异常的处理

声明异常时,建议声明更为具体的异常,这样处理的可以更具体。对方声明几个异常,就对应有几个catch块,不要定义多余的catch块。如果多个catch块中的异常出现继承关系,父类异常catch块放在最后。例如:

class Demo {
    int div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException { // 在功能上通过throws的关键字声明了该功能有可能会出现问题
        int[] arr = new int[a];

        System.out.println(arr[4]);
        return a/b; // new ArithmeticException();
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int x = d.div(5, 0); // new ArithmeticException();
            System.out.println("x="+x);
        } catch(ArithmeticException e) {
            System.out.println(e.toString());
            System.out.println("除零啦!");
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println(e.toString());
            System.out.println("角标越界啦!");
        } catch(Exception e) {
            System.out.println("haha:"+e.toString());
        } 
        System.out.println("over");
    }
}
  •  

建议在进行catch处理时,catch中一定要定义具体的处理方式。不要简单定义一句:e.printStackTrace();,也不要简单的就书写一条输出语句。

finally代码块

定义一定执行的代码。通常用于关闭资源,即无论是否有异常发生,都要对资源进行释放。释放资源的动作就定义在finally代码块中。例如:

class ExceptionDemo
{
    public static void main(String[] args) 
    {
        try
        {
            int num = 4 / 0;
            System.out.println("num = " + num);
        }
        catch (Exception e)
        {
            System.out.println(e.toString());
            return;
            // System.exit(0); // 退出JVM,只有这种情况,finally才不执行。
        }
        finally
        {
            System.out.println("finally");//如果前面执行了System.exit(0);,故不会执行此语句。
        }
        System.out.println("over");
    }
}

再看以下例子,我们可以在finally代码块中关闭数据库资源,以后必然避免不了写这样的代码。

class NoValueException extends Exception {

}
public void operate() throws NoValueException
{
    // 连接数据库
    try 
    {
        // 数据操作 throw new SQLException();
    } 
    catch (SQLException e)
    {
        // 解决了数据库异常
        throw new NoValueException();
    }   
    finally {
        // 关闭数据库
    }
}

记住一点:catch是用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。

处理异常代码块的组合方式

处理异常代码块的组合方式有多种,视具体需求而定。

  1. 没有资源需要释放,仅仅是处理异常;

    try {
        需要被检测的代码;
    } catch() {
        处理异常的代码;
    }
  2. 一个try多个catch,一般对应的是被调用的函数,抛出多个异常的情况,分别处理;

    try
    {
    
    }
    catch ()
    {
    
    }
    catch ()
    {
    
    }
    catch ()
    {
    
    }

    注意:在多catch语法上特殊的地方,如果catch中的异常类存在子父类,父类的catch一定要放在子类的下面,否则编译失败。

  3. 不一定要处理异常,但是有资源需要释放。

    try {
        需要被检测的代码;
    } finally {
        一定会指执行的代码;
    }

异常在子父类覆盖中的体现

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

异常综合案例

毕老师用电脑上课,在上课过程中有可能会发生问题,比如电脑蓝屏了、电脑冒烟了。请对问题进行描述,并封装成对象。

// 蓝屏是可以处理的,继承Exception
class LanPingException extends Exception
{
    LanPingException()
    {
        super();
    }
    LanPingException(String message)
    {
        super(message);
    }

}

// 冒烟
class MaoYanException extends Exception
{
    MaoYanException()
    {
        super();
    }
    MaoYanException(String message)
    {
        super(message);
    }

}

// 课时无法进行
class NoPlanException extends Exception
{
    NoPlanException()
    {
        super();
    }
    NoPlanException(String message)
    {
        super(message);
    }   
}

class Computer
{
    private int state = 2; //  0:正常状态

    public void run() throws LanPingException, MaoYanException
    {
        if (state == 1)
            throw new LanPingException("电脑蓝屏啦!");
        if (state == 2)
            throw new MaoYanException("电脑冒烟啦!");
        System.out.println("电脑运行");
    }

    public void reset()
    {
        System.out.println("电脑重启");
        state = 0;
    }
}
class Teacher
{
    private String name;
    private Computer comp;

    Teacher(String name)
    {
        this.name = name;
        comp = new Computer();
    }

    // 讲课
    public void prelect() throws NoPlanException
    {
        try
        {
            comp.run();
            System.out.println("讲课");
        }
        catch (LanPingException e)
        {
            System.out.println(e.toString());
            comp.reset();
            // 继续讲课
            prelect();
        }
        catch (MaoYanException e) // MaoYanException e = new MaoYanException("...");
        {
            System.out.println(e.toString());
            test();
            throw new NoPlanException("课时进度停止"); // 继续抛,但进行异常转换。封装本层异常,对外暴露对方能处理的异常。
        }
    }

    public void test()
    {
        System.out.println("练习");
    }
}

class ExceptionTest 
{
    public static void main(String[] args) 
    {
        Teacher t = new Teacher("毕老师");
        try
        {
            t.prelect();
        }
        catch (NoPlanException e)
        {
            System.out.println("换老师");
        }

    }
}

 这里记录一下,方便以后查看。
来自于https://blog.csdn.net/yerenyuan_pku/article/details/82084172

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值