Java学习12:异常

概念

程序在运行时出现不正常的情况。

异常由来

问题也是现实生活中的一个具体的事物,也可以通过java类的形式进行描述,并且封装成对象,其实就是java对不正常情况进行描述后的对象体现。
对于问题的划分有两种:严重的,java通过Error类进行描述,一般不编写针对性的代码对其进行处理;不严重的,通过Exception类进行描述,可以使用针对性的处理方式进行处理。
Throwable
|–Error
|–Exception

异常处理

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

/*
    div 0!
    / by zero
    java.lang.ArithmeticException: / by zero
    java.lang.ArithmeticException: / by zero
    at Function.div(Demo.java:178)
    at Demo.main(Demo.java:187)
    over
*/
class Function
{
    double div(int a, int b)
    {
        return a / b;//(1) new AritchmeticException()
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Function f = new Function();
        try {
            double x = f.div(1, 0);//(2) (1)传过来的new AritchmeticException()
            System.out.println("x = " + x);
        }
        catch(Exception e)//(3) Exception e = new AritchmeticException();
        {
            System.out.println("div 0!");
            System.out.println(e.getMessage());
            System.out.println(e.toString());// 异常名称:异常信息
            e.printStackTrace();// 异常名称,异常信息,异常出现的位置  其实JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。
        }
        System.out.println("over");
    }
}

对捕获到的异常对象进行常见方法操作

String getMessage() 返回此throwable的详细消息字符串
String toString() 返回此throwable的简短表现形式
void printStackTrace() 将此throwable及其追踪输出至标准错误流

throws

在函数上通过throws的关键字声明了该功能有可能会出现问题。便于提高安全性,让调用者进行处理(捕获或抛出),不处理编译失败。

class Function
{
    double div(int a, int b) throws Exception
    {
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args) //throws Exception
    {
        Function f = new Function();
        try {//不捕获编译会报错(或者在主函数后面抛出异常)  Error:(187, 29) java: 未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出
            double x = f.div(1, 0);
            System.out.println("x = " + x);
        }
        catch(Exception e)
        {
            System.out.println(e.toString());

        }
        System.out.println("over");
    }
}

对多异常的处理

  1. 声明异常时,建议声明为更具体的异常,这样处理的可以更具体。
  2. 对方声明几个异常, 就对应有几个catch块。如果多个catch中的异常出现继承关系,父类异常catch块放在最下面。
  3. 在进行catch处理时,catch中一定要定义具体处理方式,不要简单定义一句e.printStackTrace(),也不要简单的书写一条输出语句。
class Function
{
    double div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException
    {
        int[] x = new int[a];
        System.out.println(x[4]);
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args) //throws Exception  抛给虚拟机
    {
        Function f = new Function();
        try {
            double x = f.div(5, 0);
            System.out.println("x = " + x);
        }
        catch(ArithmeticException e)//java.lang.ArithmeticException: / by zero
        {
            System.out.println(e.toString());
            System.out.println("div 0!");
        }
        catch(ArrayIndexOutOfBoundsException e)//java.lang.ArrayIndexOutOfBoundsException: 4
        {
            System.out.println(e.toString());
            System.out.println("数组越界!");
        }
        System.out.println("over");
    }
}

自定义异常

因为项目中会出现特有的问题,这些问题并没有被java所描述并封装对象,所以这些特有的问题可以按照java的对问题封装的思想,将特有的问题进行自定义的异常封装。
需求:本程序中,对于除数为负数,也视为是错误的,是无法进行运算的,那么仅需要对此问题进行自定义的描述。

/*
    FuShuException
    div FuShu!
    over
*/
class FuShuException extends Exception//自定义异常
{}
class Function
{
    double div(int a, int b) throws ArithmeticException, FuShuException
    {
        if(b < 0)
            throw new FuShuException();
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args)
    {
        try
        {
            double result = new Function().div(1, -1);
            System.out.println("result = " + result);
        }
        catch(ArithmeticException e)
        {
            System.out.println(e.toString());
            System.out.println("div 0!");
        }
        catch(FuShuException e)
        {
            System.out.println(e.toString());
            System.out.println("div FuShu!");
        }
        System.out.println("over");
    }
}

上述程序中的打印结果中只有异常的名称,却没有异常的信息,因为自定义的异常中未定义信息。如何定义异常信息?
因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息通过super语句传递给父类,那么就可以直接通过getMessage方法获取自定义的异常信息(toString调用getMessage方法)。下面程序中还包含了传递特有信息。

/*
    FuShuException: / by fushu
    div FuShu!
    illegal negative value is : -1
    over
 */
class FuShuException extends Exception//自定义异常
{
      private int value;
      FuShuException(String msg, int value)
      {
           super(msg);
           this.value = value;
      }
      int getValue()
      {
          return value;
      }
//    private String msg;
//    FuShuException(String msg)
//    {
//        this.msg = msg;
//    }
//    public String getMessage()
//    {
//        return msg;
//    }
}
class Function
{
    double div(int a, int b) throws ArithmeticException, FuShuException
    {
        if(b < 0)
            throw new FuShuException("/ by fushu", b);
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args)
    {
        try
        {
            double result = new Function().div(1, -1);
            System.out.println("result = " + result);
        }
        catch(ArithmeticException e)
        {
            System.out.println(e.toString());
            System.out.println("div 0!");
        }
        catch(FuShuException e)
        {
            System.out.println(e.toString());
            System.out.println("div FuShu!");
            System.out.println("illegal negative value is : " + e.getValue());
        }
        System.out.println("over");
    }
}

注意:自定义类必须是继承Exception,继承Exception的原因:
异常体系有一个特点,因为异常类和异常对象都被抛出,他们都具有可抛性,这个可抛性Throwable这个体系中独有特点,只有这个体系中的类和对象才可以被throws和throw操作。

throws和throw的区别

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

RuntimeException

Exception中有一个特殊的子类异常RuntimeException(运行时异常)。
如果在函数内中抛出该异常,函数上可以不用声明,编译一样通过(其他异常编译不通过);
class Function
{
    double div(int a, int b)
    {
        if(b == 0)
            //throw new Exception("div zero!"); 就会报错"未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出"
            throw new ArithmeticException("div zero!");
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Function f = new Function();
        f.div(1, 0);
        System.out.println("over!");
    }
}

: 如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。

class Function
{
    double div(int a, int b) throws ArithmeticException//如果是throws Exception,则会报错“未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出”
    {
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Function f = new Function();
        f.div(1, 0);
        System.out.println("over!");
    }
}

之所以不用在函数声明,是因为不需要让调用者处理,当该异常发生,希望程序停止(我的理解就是这个异常太严重,已经无法处理),因为在运行时出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

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

//Exception in thread "main" FuShuException: div Fushu!
class FuShuException extends RuntimeException
{
    FuShuException(String msg)
    {
        super(msg);
    }
}
class Function
{
    double div(int a, int b)//throws FuShuException
    {
        if(b < 0)
            //FuShuException继承了RuntimeException,所以不用声明异常,主函数不需要处理
            throw new FuShuException("div Fushu!");
        return a / b;
    }
}
class Demo
{
    public static void main(String[] args)
    {
        new Function().div(1, -2);
    }
}

异常分为两种

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

练习

class BlueScreenException extends Exception
{
    BlueScreenException(String msg)
    {
        super(msg);
    }
}
class SmokeException extends Exception
{
    SmokeException(String msg)
    {
        super(msg);
    }
}
class NoPlanException extends Exception
{
    NoPlanException(String msg)
    {
            super(msg);
    }
}
class Computer
{
    private int status = 2;
    public void run() throws BlueScreenException, SmokeException
    {
        if(status == 2)
            throw new BlueScreenException("电脑蓝屏!");
        else if(status == 3)
            throw new SmokeException("电脑冒烟!");
        System.out.println("电脑运行!");
    }
    public void reset()
    {
        System.out.println("电脑重启!");
    }
}
class Teacher
{
    public void test()
    {
        System.out.println("练习!");
    }
    public void prelect() throws NoPlanException
    {
        Computer comp =  new Computer();
        try {
            comp.run();
        }
        catch(BlueScreenException e)
        {
            System.out.println(e.toString());
            comp.reset();
        }
        catch(SmokeException e)
        {
            test();
            throw new NoPlanException("课程无法继续," + e.getMessage());
        }
        System.out.println("讲课!");
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Teacher t = new Teacher();
        try {
            t.prelect();
        }
        catch (NoPlanException e)
        {
            System.out.println(e.toString());
            System.out.println("更换电脑");
        }
    }
}

finally代码块

定义一定执行的代码,通常用于关闭资源。(在数据库操作中将关闭数据库放在这里面)

三种格式

    //1
    try
    {
    }
    catch()
    {

    //2
    try
    {
    }
    catch()
    {
    }
    finally
    {
    }

    //3
    try
    {
    }
    finally
    {
    }

注意: catch是用于处理异常,如果没有catch就代表没有被处理过,如果该异常是检测时异常,那么必须声明,见下面程序。

class Demo
{
    public void method()
    {
        //1.编译不能通过 解决方法:(1)声明异常throws Exception;(2)捕获异常
        throw new Exception();

        //2.编译能通过,捕获异常
        try
        {
            throw new Exception();
        }
        catch (Exception e)
        {

        }

        //3.编译不能通过,异常未被声明或捕获
        try
        {
            throw new Exception();
        }
        catch (Exception e)
        {
            throw e;
        }

        //4.编译能通过,捕获异常
        try
        {
            throw new Exception();
        }
        catch (Exception e)
        {
            try
            {
                throw e;
            }
            catch(Exception e2)
            {

            }
        }

        //5.编译不能通过,异常未被声明或捕获
        try
        {
            throw new Exception();
        }
        finally
        {
            //关闭资源
        }
    }
}

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

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

异常例子

class InvalidValueException extends RuntimeException
{
    InvalidValueException(String msg)
    {
        super(msg);
    }
}
interface Shape//接口类
{
    public abstract double getArea();
}
class Rec implements Shape//长方形
{
    private double len, width;
    Rec(double len, double width)
    {
        this.len = len;
        this.width = width;
    }
    public double getArea()
    {
        if(len <= 0 || width <= 0)
            throw new InvalidValueException("输入非法值!");
        return len * width;
    }
}
class Circle implements Shape
{
    double r;
    private static final double PI = 3.14;//因为PI是共享数据,所以加static
    Circle(double r)
    {
        this.r = r;
    }
    public double getArea()
    {
        if(r <= 0)
            throw new InvalidValueException("出现非法值");
        return r * r * PI;
    }
}
class Demo
{
    public static void main(String[] args)
    {
        Rec rec = new Rec(10, 2);
        System.out.println(rec.getArea());
        Circle circle = new Circle(-11);
        System.out.println(circle.getArea());
    }
}

总结

异常是对问题的描述,将问题进行对象的封装。

异常体系:
Throwable
|–Error
|–Exception
异常体系特点:异常体系中所有类以及建立的对象都具有可抛性,也就是说可以被throw和throws关键字所操作,只有异常体系具备这个特点,

throw和throws的用法:
throw定义在函数内,用于抛出异常对象;
throws定义在函数上,用于抛出异常类,可以抛出多个,用逗号隔开。

当函数内容有throw抛出异常对象,并未进行try处理,必须要在函数上声明,否则编译失败。
注意:RuntimeException除外,也就是说,函数内如果抛出RuntimeException异常,函数上可以不用声明。如果函数声明了异常,调用者需要进行处理,处理方式可以是throws/try。

异常有两种:
编译时被检测异常,该异常如果没有处理,没有throws也没有try,编译失败,该异常被标识,代表可以被处理;
运行时异常(编译时不检测),在编译时不需要处理,编译器不检查,该异常发生时,建议不处理,让程序停止,需要对代码进行修正。

异常处理语句
try
{
需要被检测的代码;
}
catch()
{
处理异常的代码;
}
finally
{
一定会执行的代码;
}
注意:
(1)finally中定义的通常是关闭资源代码,因为资源必须释放;
(2)finally只有一种情况不会执行,当catch中写到System.exit(0)时系统退出,jvm结束,此时finally里面的语句不执行。

自定义类异常

按照java的面向对象思想, 将程序中出现的特有问题进行封装,定义类继承Exception或者RuntimeException。
(1)为了让自定义类具备可抛性;
(2)让该类具备操作异常的共性方法。

当要定义自定义异常的信息时,可以使用父类已经定义好的功能,异常信息传递给父类的构造函数。

class MyException extends Exception
{
    MyException(String msg)
    {
        super(msg);
    }
}

异常的好处
(1)将问题进行封装;
(2)将正常流程代码和问题处理代码相分离,方便于阅读。

异常的处理原则
(1)处理方式:try或者throws;
(2)调用到抛出异常的功能时,抛出几个,就处理几个,一个try对应多个catch;
(3)多个catch,父类的catch放到最下面;
(4)catch内,需要定义针对性的处理方式,不要简单地定义printStackTrace输出语句,也不要不写;
(5)当捕获到的异常,本功能处理不了时,可以继续在catch中抛出;

try
{
    cicle.getArea();
}
catch(InvalidValueException e)
{
    throw e;
}

(6)如果该异常处理不了,但并不属于该功能出现的异常, 可以将异常转换后,再抛出和功能相关的异常;或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道并处理,也可以将捕获异常处理后转换为新的异常;

//1
try
{
    cicle.getArea();
}
catch(InvalidValueException e)
{
    throw new BException();
}
//2
try
{
    cicle.getArea();
}
catch(InvalidValueException e)
{
    //对e进行处理,之后抛出新的异常
    throw new BException();
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大师兄电子工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值