认识Java异常

异常

防御式编程

在程序运行前就考虑到出错并且加以预防的编程方式

主要风格
1.LBYL

Look Before You Leap
在操作之前就充分的检查

这种风格不会使用异常处理的语法,使用的是
if-else语句

如下面这段代码

private static int divide() {
        int a = 0, b = 0;
        Scanner scanner = new Scanner(System.in);
        a = scanner.nextInt();
        b = scanner.nextInt();
        if (b == 0) {
            System.out.println("除数为0");
            return 0;
        } else {
            return a / b;
        }
    }
2.EAFP

It’s Easier to Ask Forgiveness than Permission.
事后获取原谅比事前获取许可更容易
即:先操作, 遇到问题再处理

这种风格要使用到异常处理的语法

如下面这段代码

private static int divide() {
        int a = 0, b = 0;
        try (Scanner scanner = new Scanner(System.in)) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            return a / b;
        } catch (ArithmeticException exception) {
            System.out.println("除数为0");
            return 0;
        } 
    }

其中try,catch关键字都是异常处理使用到的

异常

异常是程序运行过程中出现的错误

注意:编译错误不是异常

将代码中的

System.out.println(“除数为0”);

写成

System.out.pinrtln(“除数为0”);

这个不是异常,这个错误在编译的时候就会报错,而不是运行的时候,异常是运行的过程中才出现的

异常的种类有很多,如下

数组越界异常

int[] a = new int[]{1, 2};
System.out.println(a[100]);

结果:抛出异常

Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: Index 100 out of bounds for length 2 at Test.main

上面所举的除数为0的异常的例子

System.out.println(5 / 0);

结果:抛出异常

Exception in thread “main” java.lang.ArithmeticException: / by zero at Test.main

如果异常没有被处理而是抛给系统自动处理,系统就会打印异常,并终止程序,因此我们写代码的人就要有意识,在编写程序的时候就要处理异常,下面是处理异常的语法

异常语法
try (能够自动关闭的类型 能够自动关闭的类型的对象) {
            可能会出现异常的代码
} catch (异常类型 异常对象) {
  处理异常的代码
} finally {
  执行try-catch代码后一定执行的代码
}
void func() throws 异常类型 {
        throw 异常对象
}
  1. try代码块中放的是可能出现异常的代码
  2. catch代码块中放的是出现异常后的处理行为
  3. finally代码块中放的是处理删后的工作,比如关闭文件之类的,一定会在上面的try和catch代码块执行完毕执行
  4. catch和finally可写可不写
  5. try和catch应该一起出现,catch可以有多个
  6. throw 主动抛出异常对象
  7. throws 表示某个方法可能会抛出某些异常

例子

public class Test {

    public static void main(String[] args) {
        func();
    }
    private static void func() {
        try {
            System.out.println("异常抛出前的代码");
            String s = null;
            System.out.println(s.length());
            System.out.println("异常抛出后的代码");
        } catch (NullPointerException exception) {
            System.out.println("处理异常前的代码");
            exception.printStackTrace();
            System.out.println("处理异常后的代码");
        } finally {
            System.out.println("最后执行的代码");
        }
        System.out.println("func方法结束");
    }
}

结果

异常抛出前的代码
处理异常前的代码
处理异常后的代码
最后执行的代码
func方法结束
java.lang.NullPointerException: Cannot invoke “String.length()” because “s” is null
at Test.func
at Test.main

从中我们能看到
System.out.println("异常抛出后的代码");
这个代码没有执行,因为此时抛出了异常,所以直接跳转到了catch代码块
exception.printStackTrace();
这句话是查看出现异常的调用栈,可以理解为把异常的信息打印了出来

上面我们还有一个发现,就是

exception.printStackTrace();
System.out.println("func方法结束");
这个代码的顺序是这样的,但是打印结果确是这样的

func方法结束
java.lang.NullPointerException: Cannot invoke “String.length()” because “s” is null
at Test.func
at Test.main

出现这样的问题原因是这个打印异常信息的函数与缓冲区刷新有关,每次执行的顺序不一样

注意

如果catch没有与try抛出的异常相匹配,就会把异常继续向上抛,如果都没有处理方法,就会抛给系统

private static void func() {
        try {
            String s = null;
            System.out.println(s.length());
        } catch (ArithmeticException exception) {
            System.out.println("捕获异常");
        } finally {
            System.out.println("最后执行的代码");
        }
        System.out.println("func方法结束");
}

结果

最后执行的代码
Exception in thread “main” java.lang.NullPointerException: Cannot invoke “String.length()” because “s” is null
at Test.func
at Test.main

在try代码块中抛出的是空指针异常,但是catch所接受的是算术异常,两者不匹配,则异常会继续向上抛,向上抛就是抛给系统,然后就打印异常信息并终止程序

为了防止这样的情况,可以多写几个catch语句

private static void func() {
        try {
            String s = null;
            System.out.println(s.length());
        } catch (ArithmeticException exception) {
            exception.printStackTrace();
        } catch (NullPointerException exception) {
            exception.printStackTrace();
        } finally {
            System.out.println("最后执行的代码");
        }
        System.out.println("func方法结束");
}

也可以直接用这些异常的父类

private static void func() {
        try {
            String s = null;
            System.out.println(s.length());
        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            System.out.println("最后执行的代码");
        }
        System.out.println("func方法结束");
}

像NullPointerException,ArithmeticException等这些异常,都是继承自Exception异常,Exception异常类可以处理大多数的异常

下面看一种多次向上抛异常的情况

public class Test {

    public static void main(String[] args) {
        fun2();
    }

    private static void fun2() {
        try {
            fun1();
        } catch (InputMismatchException exception) {
            System.out.println("InputMismatchException异常处理");
        }
    }

    private static void fun1() {
        try {
            throw new InputMismatchException();
        } catch (NullPointerException exception) {
            System.out.println("NullPointerException异常处理");
        }
    }
}

结果

InputMismatchException异常处理

fun1中抛出InputMismatchException异常,fun1中无法处理,抛给它的调用者fun2,fun2可以处理异常,所以异常被处理

如果这样写,系统就会抛出异常

public class Test {

    public static void main(String[] args) {
        fun2();
    }

    private static void fun2() {
        try {
            fun1();
        } catch (InputMismatchException exception) {
            System.out.println("InputMismatchException异常处理");
        }
    }

    private static void fun1() {
        try {
            throw new ArithmeticException();
        } catch (NullPointerException exception) {
            System.out.println("NullPointerException异常处理");
        }
    }

}

结果

Exception in thread “main” java.lang.ArithmeticException
at Test.fun1
at Test.fun2
at Test.main

ArithmeticException异常fun1和fun2都不能处理,最后抛给系统处理

try with resources

在上面的语法中出现try后面出现了括号,括号里可以创建一个对象,这里就是使用try负责回收资源

try (能够自动关闭的类型 能够自动关闭的类型的对象) {
            可能会出现异常的代码
            throw 异常对象
}
...

例子

不使用try with resources,要手动调用close()方法

private static void func() {
        Scanner scanner = new Scanner(System.in);
        try {
            int a = scanner.nextInt();
            return a;
        } catch (ArithmeticException exception) {
            exception.printStackTrace();
        } finally {
            System.out.println("最后执行的代码");
            scanner.close();
        }
        System.out.println("func方法结束");
}

使用try with resources,自动调用close()方法

private static void func() {
        try (Scanner scanner = new Scanner(System.in)){
            int a = scanner.nextInt();
            return a;
        } catch (ArithmeticException exception) {
            exception.printStackTrace();
        } finally {
            System.out.println("最后执行的代码");
        }
        System.out.println("func方法结束");
}

上述两种代码要强调两点:

  1. 即使return语句执行后函数返回了,也会执行
    System.out.println("最后执行的代码");
  2. 如果关闭了scanner,下次再有用输入的话,就会抛出NoSuchElementExpection异常,最好不要关闭scanner。
finally的使用
private static int fun() {
        try {
            return 10;
        } finally {
            return 20;
        }
    }

这种情况就会出现问题,最终的值是20,最好不要在finally代码块中返回值

异常执行顺序
  1. 先执行 try 中的代码
  2. 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
  3. 如果找到匹配的异常类型, 就会执行 catch 中的代码
  4. 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
  5. 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  6. 如果上层调用者也没有处理的了异常, 就继续向上传递.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM(系统) 来进行处理, 此时程序就会异常终止
主动抛出异常

使用throw关键字

throw 异常对象

例子

public class Test {

    public static void main(String[] args) {
        try {
            fun();
        } catch (ArithmeticException exception) {
            exception.printStackTrace();
            
        }
    }
    public static void fun() {
        throw new ArithmeticException("算术异常");
    }
}
异常说明

使用throws关键字,把可能抛出的异常显示的标注在方法定义的位置,用来提醒调用者捕获异常

public class Test {

    public static void main(String[] args) throws Exception {
        fun();
    }
    public static void fun() throws Exception {
        throw new Exception("异常");
    }
}

把可能出现的异常写到函数的参数列表的后面

Java异常体系
Throwable Error Exception IOException ClassNotFoundException CloneNotSupportedException RuntimeException EOFException FileNotFoundException MalformedURLException UnknowHostException ArithmeticException ClassCastException IllegalArgumentException IllegalStateException IndexOutOfBoundsException NoSuchElementException NullPointerException

Error是系统级别的异常,也有许多子类,但是编程者不能处理,没有罗列出来

Exception是应用级别的异常,需要编程的人在编程时注意,要防范这些异常的出现

受查异常

必须显式处理的异常

处理方法:

  1. 使用try catch语句处理
  2. 通过throws把异常显示出来,让调用者捕获并处理
非受查异常

不用显示处理的异常

RuntimeException及其子类都是非受查异常
Error及其子类是非受查异常

除了这两个其他的异常主动抛出都要显示处理

public class Test {

    public static void main(String[] args) {
        fun();
    }
    public static void fun() throws Exception {
        throw new Exception("异常");
    }
}

结果

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

Exception是受查异常,这里没有进行显示处理报错了

这个是显示处理的,在main方法后面加了throws关键字,并写出了可能会抛出异常

public class Test {

    public static void main(String[] args) throws Exception {
        fun();
    }
    public static void fun() throws Exception {
        throw new Exception("异常");
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值