【Java之路】——异常处理

异常

​ 在java中用类的形式对不正常情况进行了描述和封装对象。描述不正常的情况的类,就称为异常类。以前正常流程代码和问题处理代码相结合,现在将正常流程代码和问题处理代码分离,提高阅读性。其实异常就是java通过面向对象的思想将问题封装成了对象,用异常类对其进行描述。不同的问题用不同的类进行具体的描述,比如角标越界,空指针等。问题很多,意味着描述的类也很多,将其共性进行了向上抽取,形成了异常体系,最终问题(不正常情况)就分成了两大类:error和Exception(异常);

概念

  • 在运行时期发生的不正常情况

  • 异常是一个类,产生异常就是创建异常对象并抛出一个异常对象。

    Java处理异常的方式是中断处理。

  • 异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行

异常的根类Throwable

特点

  • Throwable及其所有的子类都具有可抛性
  • 子类的后缀名都是用其父类名作为后缀,阅读性很强

分类

Error

是由jvm抛出的严重性的问题,这种问题发生一般不针对性处理,直接修改程序。

Exception(异常)

  • 特点

    异常产生后,可以通过代码的方式纠正,使程序继续运行,是必须要处理的。

  • 分类:根据在编译时期还是运行时期去检查异常 分为两种。

    • 编译时期异常:checked异常

      在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

      • 分类:除了特殊子类RuntimeException体系,Exception和其子类都是。

        • IOException
        • ClassNotFoundException
        • CloneNotSupportException
    • 运行时期异常:runtime异常

      直接编译通过,在运行时期,检查异常 。

      • RuntimeException和其子类

常用方法

  • public void printStackTrace()

    • 打印异常的详细信息。
    • 包含了异常的类型,异常的原因,还包括异常出现的位置,
  • public String getMessage()

    • 获取发生异常的原因。
    • 提示给用户的时候,就提示错误原因。

异常的产生过程

下面以角标越界异常(ArrayIndexOutofBoundsException)为例,代码如下:

public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        int num = ArrayTools.getElement(arr, 4);
        System.out.println("num=" + num);
        System.out.println("over");
    }
}

class ArrayTools {
    // 对给定的数组通过给定的角标获取元素。
    public static int getElement(int[] arr, int index) {
        int element = arr[index];
        return element;
    }
}

图解

异常发生后,函数就结束,后面的语句就不再访问,在本例中我们自己抛异常,异常信息自定义,代码如下。

class ArrayTools {
    // 对给定的数组通过给定的角标获取元素。
    public static int getElement(int[] arr, int index) {
        if(arr == null)
            throw new NullPointerException("数组的引用不能为空"); //throw抛出一个异常对象
        if(index >= arr.length)
            throw new ArrayIndexOutOfBoundsException("数组越界了:"+index);
        if(index < 0)
            throw new ArrayIndexOutOfBoundsException("数组的角标不能为负数:"+index);

        int element = arr[index];
        return element;
    }
}

抛出异常

Java程序的执行过程中如出现异常, 会生成一个异常类对象,该异常对象将被提交给Java运行时系统, 这个过程称为抛出(throw)异常。

格式:throw new 异常类名(参数)

例如:

throw new NullPointerException("要访问的arr数组不存在");
throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

异常对象的生成:

自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出

手动创建:Exception exception = new ClassCastException();创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样

举例:

package java_demo_2.exceptiondemo;

/**
 * @ClassName: Demo02_Exception
 * @author: benjamin
 * @version: 1.0
 * @description: TODO
 * @createTime: 2019/07/21/15:47
 */

public class Demo02_Exception {
    public static void main(String[] args) {
//创建一个数组
        int[] arr = {2, 4, 52, 2};
//根据索引找对应的元素
        int index = 4;
        int element = getElement(arr, index);
        System.out.println(element);
        System.out.println("over");
    }

    /*
    根据 索引找到数组中对应的元素
    */
    public static int getElement(int[] arr, int index) {
        //判断 索引是否越界
        if (index < 0 || index > arr.length - 1) {
        /*
        判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
        这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
        */
            throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~");
        }
        int element = arr[index];
        return element;
    }
}

运行结果:

那么我们通过throw将异常进行抛出后,也就是将问题返回给调用者后,对用调用者有了两种处理方法,一个是继续将问题声明出去,使用throws声明处理,另一个是捕获处理。

异常处理体系

如果异常出现的话,会立刻终止程序,所以我们得处理异常:

  1. 在方法不处理,而是声明抛出,由该方法的调用者来处理(throws)。
  2. 在方法中使用try-catch的语句块来处理异常。

声明异常throws

概念

表示当前方法不处理异常,而是提醒该方法的调用者来处理异常。

格式

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }

注意:在方法声明中用throws语句可以声明抛出异常的列表, throws后面的异常类型可以是方法中产生的异常类型, 也可以是它的父类。

throw和throws区别

  • throw

    • 抛出异常对象
    • 使用在函数内
    • throw new 异常类名(参数),举例:throw new NullPointerException("要访问的arr数组不存在");
  • throws

    • 抛出异常类
    • 使用在函数上
    • 可以抛多个,用,隔开

捕获异常try...catch...

概念

Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。

格式

try{
	编写可能会出现异常的代码
}catch(异常类型  e){
	处理异常的代码
	//记录日志/打印异常信息/继续抛出异常
}
  • 使用finally代码块

    • try...catch....finally:自身需要处理异常,最终还得关闭资源。
    • 不论在try代码块中是否发生了异常事件, catch语句是否执行, catch语句是否有异常, catch语句中是否有return,finally块中的语句都会被执行
    • 当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行
  • 注意

    多个异常采用一次捕捉多次处理

    格式

    try{
    	编写可能会出现异常的代码
    }catch(异常类型A e){ 当try中出现A类型异常,就用该catch来捕获.
    	处理异常的代码
    	//记录日志/打印异常信息/继续抛出异常
    }catch(异常类型B e){ 当try中出现B类型异常,就用该catch来捕获.
    	处理异常的代码
    	//记录日志/打印异常信息/继续抛出异常
    }
    

    注意

    • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。

    • 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

自定义异常类

概念

对于负数为角标的情况,自己自定义一个用负数角标异常来表示。负数角标这种异常在java中并没有定义过,按照java异常的创建思想,将负数角标进行自定义描述,并封装成对象。这种自定义的问题描述称为自定义异常。

一般地,用户自定义异常类都是RuntimeException的子类。

自定义异常类通常需要编写几个重载的构造器。

自定义异常需要提供serialVersionUID

自定义的异常通过throw抛出。

自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。

步骤

  1. 自定义一个异常类XxxException,继承现有的异常类

    • 自定义编译期异常 :自定义类并继承于java.lang.Exception

    • 自定义运行时期异常:自定义类并继承于java.lang.RuntimeException

  2. 使用到自定义异常的其他类

    • 方法上:throws 异常类名;

    • 方法内:throw new 异常类名(参数);

代码举例:

模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

  1. 定义一个异常类: RegisterException继承与Exceptions

  2. 模拟登录操作,使用数组模拟数据库中存储的数据,并提供当前注册账号是否尊才方法用于判断。

    // 登录异常类
    public class RegisterException extends Exception{
    
        public RegisterException() {
            super();
        }
    
        public RegisterException(String message) {
            super(message);
        }
    }
    
    //自定义一个使用异常的类
    class Demo{
        //模拟数据库中已存在的账号
        private static String[] names = {"admin","tony","benjie"};
    
        public static void main(String[] args) {
            // 从键盘输入一个名字
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入一个名字:");
            String uname = scanner.next();
            // 调用方法
            try {
                checkUserName(uname);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        public static boolean checkUserName(String uname) throws Exception{
            for(String name: names){
                if(name.equals(uname)){
                    throw new RegisterException("亲"+uname+"已经被注册了!");
                }
            }
            return true;
        }
    }
    

总结:

异常处理的5个关键字

try:执行可能产生异常的代码

catch:捕获异常

finally:无论是否发生异常,代码总被执行

throw:抛出异常。异常的生成阶段,手动抛出异常对象。throw new 异常类名(参数),例如:throw new NullPointerException("要访问的数组不存在");

throws:声明异常。异常的处理方式,声明方法可能要抛出的各种异常类。

posted @ 2019-07-21 15:58  伊万夫斯基  阅读(181)  评论(0编辑  收藏  举报