认识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 异常对象
}
- try代码块中放的是可能出现异常的代码
- catch代码块中放的是出现异常后的处理行为
- finally代码块中放的是处理删后的工作,比如关闭文件之类的,一定会在上面的try和catch代码块执行完毕执行
- catch和finally可写可不写
- try和catch应该一起出现,catch可以有多个
- throw 主动抛出异常对象
- 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方法结束");
}
上述两种代码要强调两点:
- 即使return语句执行后函数返回了,也会执行
System.out.println("最后执行的代码");
- 如果关闭了scanner,下次再有用输入的话,就会抛出NoSuchElementExpection异常,最好不要关闭scanner。
finally的使用
private static int fun() {
try {
return 10;
} finally {
return 20;
}
}
这种情况就会出现问题,最终的值是20,最好不要在finally代码块中返回值
异常执行顺序
- 先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.一直到 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异常体系
Error是系统级别的异常,也有许多子类,但是编程者不能处理,没有罗列出来
Exception是应用级别的异常,需要编程的人在编程时注意,要防范这些异常的出现
受查异常
必须显式处理的异常
处理方法:
- 使用try catch语句处理
- 通过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("异常");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122862.html