Java 中的常用工具类之异常处理
异常
什么是异常?
程序中出现的错误,意外,背离程序设计的结果
- 编译时异常
- 运行时异常
异常类:
- Throwable 根类
- Error 子类[严重错误,无法处理]
- 虚拟机错误
- 内存溢出
- 线程死锁
- …
- Exception 子类[程序可处理的异常]
- Unchecked Exception[非检查异常,编译器不强制处理]
- RuntimeException
- 空指针
- 数组越界
- 算数计算异常
- 类型转换错误
- …
- RuntimeException
- checked Exception[检查异常,编译器要求代码中处理异常]
- IO异常
- SQL数据操作异常
- …
- Unchecked Exception[非检查异常,编译器不强制处理]
- Error 子类[严重错误,无法处理]
处理
机制:
- 先抛出异常:发现异常,产生异常对象,抛出异常
- 异常对象包括
- 异常类型
- 异常出现时候程序的状态
- 异常信息…
- 异常对象包括
- 捕获异常对象:匹配可以处理异常的处理器来处理异常,匹配不到的结果是系统终止
- 检查型异常 必须捕获处理
- 非检查异常 允许不处理
- try[可能产生异常的代码]-catch[捕获异常]-finally[无论如何都要执行的逻辑]
- throw[手动抛出异常]
- throws[声明可能要抛出的异常]
- 自定义异常
- 异常链
try-catch-finally
try 可以 接多个catch ,如果不接catch,必须有finally
package com.test;
import java.util.Scanner;
public class TryDemo1 {
public static void main(String[] args) {
// 定义两个整数,输出商
Scanner sc = new Scanner(System.in);
System.out.println("~~~~~程序开始~~~~~");
try {
System.out.print("输入整数x:");
int x = sc.nextInt();
System.out.print("输入整数y:");
int y = sc.nextInt();
System.out.println("x➗y的结果为:" + x / y);
} catch (Exception e) {
System.out.println("程序出错了~~~~exception");
e.printStackTrace(); // 输出错误的堆栈信息
} finally{
System.out.println("~~~~~程序结束~~~~~");
}
}
}
当输入y为0或者输入的数据无法转换成Int,就会捕获异常,然后执行catch的代码块
多重catch
package com.test;
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryDemo1 {
public static void main(String[] args) {
// 定义两个整数,输出商
Scanner sc = new Scanner(System.in);
System.out.println("~~~~~程序开始~~~~~");
try {
System.out.print("输入整数x:");
int x = sc.nextInt();
System.out.print("输入整数y:");
int y = sc.nextInt();
System.out.println("x➗y的结果为:" + x / y);
} catch (ArithmeticException e) {
// ArithmeticException 捕获算术异常
System.out.println("程序出错了~~~~算术异常");
e.printStackTrace(); // 输出错误的堆栈信息
} catch (InputMismatchException e) {
// InputMismatchException 捕获输入数据的格式异常
System.out.println("程序出错了~~~~输入格式异常");
e.printStackTrace(); // 输出错误的堆栈信息
} catch (Exception e) {
// 不能保证上面两个异常可以被明确捕获,那么最后需要用一个父类来捕获漏网之鱼
System.out.println("Exception 异常");
} finally {
System.out.println("~~~~~程序结束~~~~~");
}
}
}
finally 块 无论是否出错都会执行相应的代码,能够阻断finally块的方法有
- System.exit(1) 强制退出
当try catch finally 块中都存在return 语句的时候,无论是否发生异常,都将return finally块中的结果
throws 和 throw
- throws 声明抛出异常的类型
- throw 手动抛出异常
throws:
结构:
public void method() throws Exception1,Exception2,... {
// 可能产生异常的代码
}
这种处理异常的方式不是 method()方法内部来实现对异常的捕获和处理的,上述的格式的意思是,调用这个方法可能会出现某些异常,
需要调用者对可能出现的异常进行捕获或者处理
// 使用 throws
package com.test;
import java.util.InputMismatchException;
import java.util.Scanner;
public class tryDemo2 {
public static void main(String[] args) {
try {
int res = test();
System.out.println("x/y=" + res);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
e.printStackTrace();
} catch (InputMismatchException e) {
System.out.println("输入数据必须为数字");
e.printStackTrace();
}
}
public static int test() throws ArithmeticException, InputMismatchException {
Scanner sc = new Scanner(System.in);
System.out.println("=====程序开始=====");
System.out.print("输入整数x:");
int x = sc.nextInt();
System.out.print("输入整数y:");
int y = sc.nextInt();
System.out.println("=====程序结束=====");
return x / y;
}
}
可以直接throws父类异常,根据多态的方式去处理各个异常,但是需要明确的有处理父类异常的catch
package com.test;
import java.util.InputMismatchException;
import java.util.Scanner;
public class tryDemo2 {
public static void main(String[] args) {
try {
int res = test();
System.out.println("x/y=" + res);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
e.printStackTrace();
} catch (InputMismatchException e) {
System.out.println("输入数据必须为数字");
e.printStackTrace();
} catch (Exception e){
// 显式的catch Exception 处理 否则会报错
e.printStackTrace();
}
}
/**
* 这是一个测试两个数据做除法结果的方法
* @return 数的商
* @throws Exception
*/
public static int test() throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("=====程序开始=====");
System.out.print("输入整数x:");
int x = sc.nextInt();
System.out.print("输入整数y:");
int y = sc.nextInt();
System.out.println("=====程序结束=====");
return x / y;
}
}
为什么使用Exception会强制处理异常?
因为Exception中包括checkException,检查异常是必须去处理的
注意: 当子类重写父类带有抛出异常的方法的时候,子类方法的throws必须声明的异常是父类throws声明异常的同类或者子类
throw 抛出异常对象
throw new IOException();
注意: 只能抛出 可抛出类Throwable或者其子类的实例对象.
格式1:
public void method(){
try{
// code 1
throw new ExceptionType();
} catch(ExceptionType e){
// code 2
} finally{
// code 3
}
}
格式2:
public void method() throws ExceptionType{
// code 1
throw new ExceptionType();
}
通过手动抛出异常,捕获异常的方式,来实现某些业务需求.
自定义异常
定义一个异常类,继承于 Throwable 或者 他的子类
public class MyException extends Exception {
public MyException() {
super("这个异常是自定义异常"); // 异常信息 对应e.getMessage()
}
}
异常链
一层一层的抛出的异常链条
- 在捕获一个异常A之后,抛出另外一个异常B
- testOne() 触发 one
- testTwo() 处理 异常one 抛出异常 two
- testThree() 处理异常two …
package com.test;
public class TryDemo3 {
public static void main(String[] args) {
try {
testThree();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testOne() throws MyException {
throw new MyException();
}
public static void testTwo() throws Exception {
try {
testOne();
} catch (MyException e) {
throw new Exception("我是异常two");
}
}
public static void testThree() throws Exception {
try {
testTwo();
} catch (Exception e) {
throw new Exception("我是异常three");
}
}
}
执行的结果是
java.lang.Exception: 我是异常three
at com.test.TryDemo3.testThree(TryDemo3.java:29)
at com.test.TryDemo3.main(TryDemo3.java:6)
发现只是有最后一个异常的message,前面异常信息都丢失了,想要连带的保留异常信息的方式有.
在形成新异常对象的时候将旧的异常作为参数传入 throw new Exception("new exception",e)
上述的代码中将e作为参数传入的结果是:
java.lang.Exception: 我是异常three
at com.test.TryDemo3.testThree(TryDemo3.java:29)
at com.test.TryDemo3.main(TryDemo3.java:6)
Caused by: java.lang.Exception: 我是异常two
at com.test.TryDemo3.testTwo(TryDemo3.java:21)
at com.test.TryDemo3.testThree(TryDemo3.java:27)
... 1 more
Caused by: com.test.MyException: 这个异常是自定义异常
at com.test.TryDemo3.testOne(TryDemo3.java:14)
at com.test.TryDemo3.testTwo(TryDemo3.java:19)
... 2 more
或者通过详细的方式去new 新的异常
catch (MyException e){
Exception newE = new Exception("new exception");
newE.initCause(e);
throw newE;
}