目录
一、异常类的层级结构
Java中的异常其实一般来说是说的Exception异常,这是微观的,其实宏观上面的异常还包括Error
反正就是不正常。注意Java中的异常并不是代码的语法错误,而是说为了预防运行时出现错误,或者是运行时出现错误
宏观上面的异常分为两个大类:
总的来说都是Throwable类下面的两个子类
Error和Exception
Error指的是错误,一些无法直接解决的错误,通常是很严重的错误,会导致程序无法正常运行
比如是 OOM内存溢出 StackOverFlow栈溢出等异常
而Exception是异常,可以直接解决的,分为编译时异常也叫做受检异常和运行时异常也叫非受检异常
编译时异常时为了预防程序发生错误而出现了这么一种异常,当抛出了编译时异常,则编译器不能让你通过,必须得手动的处理掉这个异常(try-catch或者失败throws的方式)。
常见的编译时异常有:
FileNotFountException和ClassNotFoundException、SQLExcption
而运行时异常则是当程序真正的执行出错了抛出的异常,RuntimeException及其子类异常都是运行时异常,否则就是编译时异常 常见的运行时异常有:
NullPointException空指针异常、IndexOutOfBoundsException数组越界异常
ClassCastException 类型转换异常、NumberFormatException字符串转换异常、ArithmaticExcepion算数异常等
二、受检异常和非受检异常的区别
受检异常必须手动将其处理掉,否则编译器都不能让你通过 它是为了避免出现异常的这么一种异常
而运行时异常可以不处理 它可以指定程序执行时候出现的错误
其实两者都是要程序真正执行才能产生异常。只是说一个是预防,不管有没有提前就需要处理
而另外一个则是可以不急着处理,因为这类异常并不是很频繁,当真正出现了问题再去修改即可
就好像说 因为下雨其实是经常的所以需要提前备好雨伞,但是像地震就发生得少,所以可以不急着预防
三、throw和throws的区别
throw是抛出一个异常是产生异常
throws是将将异常进行处理,将异常抛出给调用者
四、try-catch-finally的使用
try块中是放代码,如果里面出现了异常,交由catch块处理,而finnaly则是一定会执行的代码
注意:再try或者是再catch中有return的话 那么先将结果存到本地变量中,然后执行完finnally在返回。如果说再finally中有return的话那么会覆盖掉之前的结果
五、finally一定会执行吗
不一定 当在之前调用了System.exit方法或者是cpu关闭或者是当前线程死亡就不能执行
六、异常的底层
java
public static void simpleTryCatch() {
try {
testNPE();
} catch (Exception e) {
e.printStackTrace();
}
}
通过查看字节码指令就会知道,有一个异常表:
java
//javap -c Main
public static void simpleTryCatch();
Code:
0: invokestatic #3 // Method testNPE:()V
3: goto 11
6: astore_0
7: aload_0
8: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V
11: return
Exception table:
from to target type
0 3 6 Class java/lang/Exception
- from 可能发生异常的起始点
- to 可能发生异常的结束点
- target 上述from和to之前发生异常后的异常处理者的位置
- type 异常处理者处理的异常的类信息
七、Java7的try-with-resource
这个是简化之前的try-finally的
这个只要流实现了Closeable接口或者是AutoCloseable接口 都能自动的关闭资源 然后执行catch或者是finally中的语句 而不需要我们再手动的写关闭资源的代码
语法也就是直接将流的声明写在try的()后面,多个用逗号分隔
java
try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
int b;
while ((b = bin.read()) != -1) {
bout.write(b);
}
}
catch (IOException e) {
e.printStackTrace();
}
八、Throwable类的常用方法
toString:显示异常对象的详细信息
getMessage:显示异常的简要信息
getLocalizedMessage:显示异常的本地化信息,如果其Throwable的子类覆盖了这个方法,那么就能生成本地化信息,否则和getMessage相同
printStackTrack:打印异常对象封装的异常堆栈信息
九、使用异常机制需要注意那些?
-
异常信息一定要有意义
-
抛出的异常应该具体一点 比如说能抛出NumberFormatException就不要抛出IllegalArgumentException
-
使用日志打印异常之后就不要再抛出异常了
-
不要将异常定义为静态的,这样会使得异常的栈信息混乱,应该使用throw new的方式
手动的抛出异常对象。
比如说下面这个例子:
java
public class Exceptions {
public static BusinessException ORDEREXISTS = new BusinessException("订单已经存在", 3001);
...
}
@GetMapping("wrong")
public void wrong() {
try {
createOrderWrong();
} catch (Exception ex) {
log.error("createOrder got error", ex);
}
try {
cancelOrderWrong();
} catch (Exception ex) {
log.error("cancelOrder got error", ex);
}
}
private void createOrderWrong() {
//这里有问题
throw Exceptions.ORDEREXISTS;
}
private void cancelOrderWrong() {
//这里有问题
throw Exceptions.ORDEREXISTS;
}
结果:
java
[14:05:25.782] [http-nio-45678-exec-1] [ERROR] [.c.e.d.PredefinedExceptionController:25 ] - cancelOrder got error
org.geekbang.time.commonmistakes.exception.demo2.BusinessException: 订单已经存在
at org.geekbang.time.commonmistakes.exception.demo2.Exceptions.<clinit>(Exceptions.java:5)
at org.geekbang.time.commonmistakes.exception.demo2.PredefinedExceptionController.createOrderWrong(PredefinedExceptionController.java:50)
at org.geekbang.time.commonmistakes.exception.demo2.PredefinedExceptionController.wrong(PredefinedExceptionController.java:18)
实际上我们需要的是create方法的日志记录应该是createOrder got error而非cancelOrder got error
就造成不匹配的混乱情况