异常定义
按照面向对象的思想对程序运行时其发生的不正常情况进行描述和对象的封装
除非你能解决(或必须要处理)这个异常,否则不要捕获它,如果打算记录错误消息,那么别忘了把它再抛出去
分类
Throwable是所有异常的父类
Error表示很严重的问题发生了,可以捕获但是不要捕获,因为捕获了也解决不了,这个不是由程序产出的,底层出现问题就让他它挂了吧。
关键字
throws:用来声明异常,作用在方法上,后面跟的是异常类
throw:用来抛出异常,作用在方法内,后面跟的是异常对象,throw之后的语句不会被执行
解决异常
异常的处理方式有两种,一种是在方法层面抛出,由调用者处理。一种是捕获try{}catch{}finally{}
而对于运行时异常以及他的子类来说可以不声明也不捕获,目的就是让程序停止
// 直接抛出
private static String toStringUTF8() throws UnsupportedEncodingException {
return new String(new byte[1024], "utf8");
}
// 捕获
private static String toStringUTF8(){
try {
return new String(new byte[1024], "utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
具体场景:
1.对于认定一定不会发生的异常
// 字节数组转通过UTF-8解码成字符串的工具类
// jvm必然能识别utf8的编码格式,但是如果写成了ufo8呢?所以必须打印日志
public static String toStringUTF8(byte[] bytes){
try {
return new String(bytes, "utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace(); // 1.捕获并且打印相关日志 2.捕获但什么都不做
}
return null;
}
2.对假定不应该出现的异常
// 字符集由调用者传入,可能出现错误的字符集
// 直接抛异常或者捕获打印日志都不好,调用者并不清楚是自己传错了只能事后查看日志,应该抛出运行时异常
public static String toStringUTF8(byte[] bytes, String charsetName){
try {
return new String(bytes, charsetName);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // 抛出运行时异常
}
}
3.对假定一定会出现问题的异常
拿回去重写
自定义异常
// 继承Exception或者RuntimeException,重写常规方法
public class MyException extends RuntimeException {
public MyException() {
}
// 异常信息
public MyException(String message) {
super(message);
}
// cause:异常链 也就是导致该异常发生的异常
public MyException(String message, Throwable cause) {
super(message, cause);
}
public MyException(Throwable cause) {
super(cause);
}
}
异常链就是把原始的异常包装为新的异常类,并在新的异常类中封装了原始异常类,这样做的目的在于找到异常的根本原因
// 由着凉异常导致了感冒异常导致了发烧异常
public static void main(String[] args) {
throw new MyException("发烧",
new MyException("感冒",
new NullPointerException("着凉")));
}
Exception in thread "main" com.example.demo.MyException: 发烧
at com.example.demo.MyException.main(MyException.java:28)
Caused by: com.example.demo.MyException: 感冒
... 1 more
Caused by: java.lang.NullPointerException: 着凉
... 1 more
try…catch…finally
// 只是捕获异常
try {} catch (Exception e) {}
// 不处理异常,但是需要释放资源,必须声明异常(finally中的代码一定会执行)
try {} finally {}
// 捕获多个异常,如果catch()中的异常类存在父子异常,则子异常一定要放在上面
try {} catch (NullPointerException e) {} catch (Exception e) {}
执行顺序:
在同一try…catch…finally中,如果try抛出了异常且有匹配的catch,则先执行catch再执行finally,
如果没有匹配的catch则先执行finally再去外部调用者寻找catch,
如果catch匹配成功但是也抛出了异常,还是先执行finally再去外部调用者寻找catch
注意:
finally中的return会覆盖try或catch中的return,也会抑制try或catch中的异常(当做没异常发生)
finally中的异常会覆盖try或catch中的异常
finally中的代码一定会执行,除非JVM挂了,或者执行了System.exit(0)
子类重写父类的方法,只能抛出父类的异常或者异常的子类。如果父类没有抛出异常,则子类只能捕获。
try-with-resources
// jdk7的新语法,在try()中声明的资源会自动关闭
try (FileInputStream fis = new FileInputStream("")) {} catch (Exception e) {}
通过使用分号分隔,可以在try-with-resources块中声明多个资源
适用范围(资源的定义): 任何实现 java.lang.AutoCloseable
或者``java.io.Closeable` 的对象
关闭资源和final的执行顺序: 在 try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行
Throwable三个常用方法
// 异常信息 / by zero
System.out.println(e.getMessage());
// 异常类名 + 异常信息 java.lang.ArithmeticException: / by zero
System.out.println(e.toString());
// 异常类名 + 异常信息,以及异常出现在程序中的位置(最常用的)
e.printStackTrace();
log中打印异常的正确姿势
try {
} catch (MyException e) {
log.error("invoke error:", e); // 能够打印出完整的堆栈信息
}