1

第四阶段 IO

异常处理

没有完美的程序,所以我们需要不断地完善,考虑各种可能性,我们要将除了自己以外的任何用户或者操作者都当成傻子来考虑问题

在我们开发过程中 我们运行时常常会遇到 这样java.lang.XxxException的信息,这代表程序发生了一些错误,也叫作异常

(一) 概述及体系

(1) 概述
异常情形是指阻止当前方法或者作用域继续执行的问题 ——《Think in Java》

简单概括:程序出现不正常情况后,程序将会跳出当前环境,并且抛出异常。

(2) 体系

(1) Error —— 错误:程序无法处理的严重错误,我们不作处理

  • 这种错误一般来说与操作者无关,并且开发者与应用程序没有能力去解决这一问题,通常情况下,JVM会做出终止线程的动作

(2) Exception —— 异常:异常可以分为运行时异常和编译期异常

  • RuntimeException:即运行时异常,我们必须修正代码

    • 这些异常通常是由于一些逻辑错误产生的

      这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,但是程序员 也可以根据需要行捕获抛出,(不受检查异常)这类异常通常是可以被程序员避免的。
      常见的RUNtimeException有:NullpointException(空指针异常),ClassCastException (类型转 换异常),IndexOutOfBoundsException(数组越界异常)等。

  • 非RuntimeException编译期异常,必须处理,否则程序编译无法通过

    • 这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。
    • 常见编译异常有:IOException(流传输异常),SQLException(数据库操作异常)等。

Java内置异常类

A:Java 的非检查性异常

异常 描述
ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
ClassCastException 抛出表示代码尝试将对象转换为不属于实例的子类。 例如:将Integer型对象转换为String类
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
NumberFormatException 抛出以表示应用程序已尝试将字符串转换为其中一个数字类型,但该字符串不具有相应的格式。
IllegalStateException 表示在非法或不适当的时间调用了一种方法。 换句话说,Java环境或Java应用程序对于请求的操作并不处于适当的状态。
IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 应用程序可以将此类子类化以指示类似的异常。
ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
NoSuchElementException 被各种访问器方法抛出,表示被请求的元素不存在。
InputMismatchException 输入类型不匹配异常,一般出现此类异常的原因就是定义的接受输入值的变量的类型与输入的数值的类型不匹配导致的异常。
NullPointerException 空指针异常

B:Java的检查性异常

异常 描述
ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。

(二) 异常处理语句

(1) try-catch

A:捕获单个异常

try{
    //程序代码
}catch (ExceptionName e1){
    //Catch块
}

B:多重捕获块

try{
    //程序代码
}catch (异常类型1 异常的变量名1){
    ......
}catch (异常类型2 异常的变量名2){
    ......
}catch (异常类型3 异常的变量名3){
    ......
}

try:用于监听异常

catch:用于捕获处理异常

try 后面的一对大括号内的内容即受到监控的程序,也就是说这一段代码是可能存在异常的,而Catch中由开发者,根据自身推断可能存在的异常,书写对应异常类型以及处理方式,当程序发生异常时,会创建异常对象,并且将异常抛出到此环境外,Java运行时系统会寻找与发生异常所匹配的catch子句,当找到一个对应语句后则不再寻找其他catch块

public class Demo {
    public static void main(String[] args) {
        int a = 520;
        int b = 0;
        int c;

        try {
            System.out.println("这是一个被除数为0的式子");
            c = a / b;
        } catch (ArithmeticException e) {
            System.out.println("除数不能为0");
        }
    }
}

//运行结果
这是一个被除数为0的式子
除数不能为0

通过上例可以看到,被监控的语句中先依次正常执行,当遇到存在问题的语句时,找到匹配异常,并且执行catch块中的语句

而一般来说我们会在catch语句块中通过异常对象执行异常方法

方法方法 说明
public String getMessage() 回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了
public Throwable getCause() 返回一个Throwable 对象代表异常原因
public String toString() 使用getMessage()的结果返回类的串级名字
public void printStackTrace() 打印toString()结果和栈层次到System.err,即错误输出流

我们还是用上面的例子给出异常方法的测试

System.out.println(e.getMessage());

/ by zero
System.out.println(e.getCause());

null
System.out.println(e.toString());

java.lang.ArithmeticException: / by zero
e.printStackTrace();

java.lang.ArithmeticException: / by zero
    at cn.bwh_01_Throwable.Demo.main(Demo.java:10)
(2) try-catch-finally

我们在 try-catch的基础上再补充一个finally的知识

finally 关键字用来创建在 try 代码块后面执行的代码块无论是否发生异常,finally 代码块中的代码总会被执行,在 finally 代码块中,可以运行清理类型等收尾善后性质的语句,finally 代码块出现在 catch 代码块最后,语法如下:

try{
    ......
}catch(异常类型1 异常的变量名1){
    ......
}catch(异常类型2 异常的变量名2){
    ......
}finally{
    ......
}

无论是否发生异常,fianlly始终都是会运行的

这里就不得不提一个非常重要的注意点,那就是当return遇到finally

注意点
A:return遇到finally

我们来看下面一个例程:

public class Demo2 {
    public static void main(String[] args) {
        System.out.println(test());
    }

    public static String test(){
        int[] array = new int[2];
        try{
            array[3] = 0;
            return "This is try";
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println(e);
            return "This is catch 1";
        }catch (Exception e){
            System.out.println(e);
            return "This is catch 2";
        }finally {
            System.out.println("This is finally");
            //return "This is finally's return";
        }
    }
}

//运行结果
java.lang.ArrayIndexOutOfBoundsException: 3
This is finally
This is catch 1

由此我们得出一个结论:在catch中遇到return时,仍然会先执行finally语句,再回来执行对应catch语句中的return语句

但是我们如果将finally中return行中的注释去掉,运行结果就会变成下面这样子

//运行结果
java.lang.ArrayIndexOutOfBoundsException: 3
This is finally
This is finally's return

结果就是返回值会被finally中的return语句重新覆盖

B:catch 顺序问题
public class Demo3 {
    public static void main(String[] args) {
        int[] array = new int[2];
        try{
            array[5] = 0;
        }catch (Exception e){

        }catch (ArrayIndexOutOfBoundsException e){

        }
    }
}

//运行结果
Error:(8, 10) java: 已捕获到异常错误java.lang.ArrayIndexOutOfBoundsException

对于多个catch的情况,当try中程序发生异常,会按照从上往下的顺序与catch进行匹配,一旦与其中一个匹配后就不会再与后面的catch进行匹配了,所以,在书写catch语句的时候,一定要把范围小的放在前面,范围大的放在后面!

(3) throw/throws

将自己处理不了的,在方法或者语句上声明,告诉调用者,这里有问题

如果一个方法没有捕获到检查性异常,那么该方法就必须使用throws关键字声明(声明抛出多个异常,之间使用逗号隔开),也可以在语句中使用throw关键字抛出一个异常。

//演示throw
public class Demo4 {
    public static void main(String[] args) {
        test();
    }

    public static void test() {
        int a = 520;
        int b = 0;
        if (b == 0) {
            throw new ArithmeticException();
        } else {
            System.out.println(a / b);
        }
    }
}

//运行结果
Exception in thread "main" java.lang.ArithmeticException
//演示throws

public class Demo4 {
    public static void main(String[] args) {
        try {
            test();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void test() throws Exception {
        int a = 520;
        int b = 0;
        if (b == 0) {
            throw new Exception();
        } else {
            System.out.println(a / b);
        }
    }
}

//运行结果
java.lang.Exception
throws和throw的区别

A:throws

  • 用在方法声明后,跟的是异常类名
  • 可以跟多个异常类名,用逗号隔开
  • 表示抛出异常,由该方法的调用者来处理
  • throws表示出现异常的一种可能性,不一定会发生这些异常

B:throw

  • 用在方法体内,跟的是异常对象名
  • 只能抛出一个异常对象名
  • 表示抛出异常,由方法体内的语句处理
  • 执行throw则一定抛出了某种异常

(三) 自定义异常

Java内置的异常类,基本可以满足我们在编程中遇到的大部分异常情况,此外我们还可以自定义异常类,只需要继承Exception类即可

基本步骤:

  • 创建自定义异常类
  • 在方法中通过throw抛出异常对象

    • 在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获处理
    • 也可以在方法声明处使用throws将异常抛给方法调用者
  • 在出现异常方法的调用者中捕获并且处理异常
class MyException extends Exception {  // 自定义的类
    MyException(String s) {
        super(s);
    }
}
class Demo {
    void method() throws MyException {
        throw new MyException("Wrong"); // 抛出自定义的类
    }
}
class DemoTest {
    public static void main(String[] args){
        try {
            new Demo().method();
        }
        catch(MyException e) {
            System.out.println(e.getMessage());
        }
    } 
}

//运行结果
Wrong

(三) 总结

异常就是在程序发生异常时,强制终止程序运行,并且将异常信息返回,由开发者决定是否处理异常

简单说一下这个异常机制的过程:

当程序无法运行后,它会从当前环境中跳出,并且抛出异常,之后,它会先new一个异常对象,然后在异常位置终止程序,并且将异常对象的引用从当前环境中返回,这时候异常处理机制接管程序,并且开始寻找可以继续执行程序的恰当位置。

结尾:

如果内容中有什么不足,或者错误的地方,欢迎大家给我留言提出意见, 蟹蟹大家 !^_^

如果能帮到你的话,那就来关注我吧!(系列文章均会在公众号第一时间更新)

在这里的我们素不相识,却都在为了自己的梦而努力 ❤

一个坚持推送原创Java技术的公众号:理想二旬不止


二境志
191 声望26 粉丝