计算机的异常
需求:输入两个值进行除法计算
Scanner input = new Scanner(System.in); System.out.print("请输入第1个操作数:"); int num1 = input.nextInt(); System.out.print("请输入第2个操作数:"); int num2 = input.nextInt(); // 计算结果 int result = num1 / num2; System.out.printf("%d / %d = %d\n", num1, num2, result);
正常情况
请输入第1个操作数:10
请输入第2个操作数:2
10 / 2 = 5异常情况1
请输入第1个操作数:a
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextDouble(Scanner.java:2413)
at demo03.Demo01.main(Demo01.java:12)异常情况2
请输入第1个操作数:10
请输入第2个操作数:0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at demo03.Demo01.main(Demo01.java:17)采用if解决异常
Scanner input = new Scanner(System.in); System.out.print("请输入第1个操作数:"); if (!input.hasNextInt()) { System.out.println("对不起!请输入整数!"); return; } int num1 = input.nextInt(); System.out.print("请输入第2个操作数:"); if (!input.hasNextInt()) { System.out.println("对不起!请输入整数!"); return; } int num2 = input.nextInt(); if (num2 == 0) { System.out.println("对不起!除数不能为0!"); return; } // 计算结果 int result = num1 / num2; System.out.printf("%d / %d = %d\n", num1, num2, result);
分析
核心业务甚至不如错误判断代码多
由于添加了大量的判断,导致代码阅读性大大降低
即使添加了大量的 if 判断,也仅仅是一个查缺补漏的环节,并不能够保证所以异常都能被处理
Java异常处理机制
Java 提供了一套专业的异常处理机制。
5个关键词以及一套成熟的异常类的体系结构(以 Exception 类为顶级父类)。
-
try:尝试
-
catch:抓住
-
finally:最终地
-
throw:扔、抛
-
throws:扔、抛
try-catch(必会)
try { // 可能会出现异常的代码段 // 尝试抓住异常的代码段 } catch(异常类型 变量名) { // 抓住异常后,如何处理异常 } // 后续的代码段
在执行 try 块中的代码段时,如果没有出现任何异常,执行完 try 块后继续向下执行 try-catch 后的内容。
而如果在 try 块中出现异常,try 块后的代码不再执行,直接由 catch 抓住相应异常,然后进行异常处理,但是,如果 catch 声明的异常类型和实际出现的异常不匹配,那么异常采用默认形式处理。处理完异常,会继续向下执行 try-catch 后的内容。
使用 try-catch 优化后的需求。
Scanner input = new Scanner(System.in); try { System.out.print("请输入第1个操作数:"); int num1 = input.nextInt(); System.out.print("请输入第2个操作数:"); int num2 = input.nextInt(); // 计算结果 int result = num1 / num2; System.out.printf("%d / %d = %d\n", num1, num2, result); } catch (InputMismatchException e) { System.out.println("对不起!请输入整数!"); }
多重catch
try { } catch(异常类型1 变量名) { } catch(异常类型2 变量名) { }
catch 可以写多个,指定不同的异常类型,这样就可以处理不同类型的异常了。
它的判断机制和多重 if 类似,也是自上而下判断异常类型,只要有一个异常类型匹配,后续的就不再进行判断和处理了。
一般在多重 catch 最后还会添加一个 Exception 类型的捕获,因为多态(一切使用父类引用的地方,都可以传入其子类对象),所以所有的异常类型都可以匹配到 Exception 类型。
由于 Exception 类型是异常类型的顶级父类,所以一般没有特殊需求处理多种异常情况,可能在 catch 中只会声明捕获 Exception 这一个。
try { } catch(Exception e) { }
常见的异常处理
直接输出异常提示(System.out.println())
使用异常输出(System.err.println(),红色输出)
打印异常堆栈跟踪/信息(默认 JVM 就是这么处理的)
printStackTrace() : void
getMessage() : String
有些异常抛出时没有提示信息你自己打印和 JVM 默认打印的区别在于,JVM 默认打印完会中断程序
// 异常发生在 main/主 线程 // 异常类型:异常提示信息 // 异常出现具体位置:类、方法、行号 Exception in thread "main" java.lang.ArithmeticException: / by zero at demo04.Demo01.main(Demo01.java:19)
// 异常出现的具体位置提示,越向上,越是根本原因,越向下,越是直接原因。java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:864) at java.util.Scanner.next(Scanner.java:1485) at java.util.Scanner.nextInt(Scanner.java:2117) at java.util.Scanner.nextInt(Scanner.java:2076) at demo04.Demo04.main(Demo04.java:14)
try-catch-finally
finally:无论是否出现异常,都会执行 finally 中的内容。
finally 中一般编写释放资源类的代码。
finally 和 return 在 try-catch 代码块中同时出现,它会先执行 finally 再执行 return。try { // 可能出现异常的代码段 } catch (异常类型 变量名) { // 异常处理 } finally { // 无论是否出现异常,都希望能执行 }
throw
在使用 JDK 提供的这些类时,如果出现了异常,实际上是 JDK 的这些类的代码中抛出了异常。
而如果我们以后写了一些程序,也出现了不合理的情况,我们也可以手动抛出异常,告诉调用者有什么问题。
try-catch是积极的处理方法,它主张发现异常,并处理异常。
throw 抛异常是一种消极的处理方法,它主张发现异常,抛上去让调用者解决。
但有些时候,却是是需要向上抛出,来让调用者知道你这有什么问题,才能进行合理的处理。
抛出异常对象的,一般配合 if 使用,在发现不合理的情况时,创建并抛出异常。
throw 异常对象;
throws
声明异常,声明该方法可能出现哪些异常。和 throw 一样都是一种相对消极的异常处理方法,让调用者来处理。
大多数用来标注方法中出现的受检异常。运行时异常一般通过 throw 直接把异常对象抛上去了。
访问权限修饰符 返回值类型 方法名(形式参数列表) throws 异常类型, ... { }
Java中的异常体系结构
Throwable:可抛的
Error:错误,是必须通过修改代码或修改环境才能解决的问题
Exception:顶级父类,异常还可以修复
RuntimeException 运行时异常 ,不要求立即处理,运行时可能出错,也可能不出错
InputMismatchException:输入不匹配异常
IllegalArgumentException:非法参数异常
NullPointerException:NPE,空指针异常
ClassCastException:类型转换异常
ArrayIndexOutofBoundsException:数组下标越界异常
受检异常,要求立即处理(因为 Java 认定这种错误很可能出现)
FileNotFoundException 文件找不到异常
SQLException
...
自定义异常:
自定义异常处理,根据需求决定
自定义异常 is a Exception类/RuntimeException类
public class IllegalHealthException extends RuntimeException { public IllegalHealthException() { super(); } public IllegalHealthException(String message) { super(message); } }