异常
常见异常
NullPointerException(空指针异常)
- 当发现异常如s的值为null,启动异常处理机制,首先创建一个异常对象,查找看谁能处理这个异常,如果没有,则java启动默认处理机制,即打印异常栈信息到屏幕,并退出程序。
- 异常栈信息包括了从异常发生点到最上层调用者的轨迹,还包括行号。
NumberFormatException(数字格式异常)
- throw关键字可以与return关键字进行对比。return代表正常退出,throw代表异常
- 异常处理机制会从当前函数开始查找看谁"捕获"了异常,当前函数没有就查看上一层,直到主函数,如果主函数也没有,就是用默认处理机制,即输出异常栈信息并退出。
异常类
NullPointerExcwption和NumberFormatException都是异常类,所有异常类都有一个公共的父类Throwable。
Throwable
- Throwable有四个构造方法
public Throwable()
public Throwable(String message)
public Throwable(String message, Throwbale cause)
public Throwable(Throwable cause)
- Throwable类有两个主要参数:一个是message,表示异常消息;另一个是cause,表示出发该异常的其他异常。
- 异常可以形成一个异常链,上层的异常由底层异常触发,cause表示底层异常。
- Throwable还有一个public方法用于设置cause:
- Throwable initCause(Throwable cause)
- Throwable的某些子类没有带cause参数的构造方法,就可以通过这个方法来设置,这个方法最多只能被调用一次。在所有构造方法的内部,都有一句重要的函数调用:
- fillInStackTrace();
- 它会将异常栈信息保存下来,这是我们看到异常栈的关键。
- Throwable有一些常用方法用于获取异常信息,比如:
- void printStackaTrace(PrintStream s)
- void printStackTrace(PrintWriter s)
- String getMessage() //获取设置的异常message
- Throwable getCause() //获取异常的cause
- //获取异常栈每一层的信息,每个StackTraceElement包括文件名、类名、函数名、行号等信息
- StackTraceElement[] getStackTrace()
异常类体系:
- Throwable是所有异常类的基类,它由两个子类:Error和Exception
- Error表示系统错误或内存耗尽,由Java系统自己使用。
- Exception表示应用程序错误,它有很多子类,也可以继承或其子类创建自定义异常。
- Exception三个直接子类:
- IOException(输入输出I/O异常)、Runtime Exception(运行时异常)、SQLException(数据库SQL异常)
- 其中RuntimeException是未受检异常、Exception其他子类和它自身是受检异常(checked exception),Error子类及其自身也是未受检异常。
- 受检和未受检区别在于Java如何处理这两种异常。
- 受检异常,Java会强制要求程序员进行处理,否则会有编译错误。
- 未受检异常,无此要求
自定义异常
- 如果继承的是Exception及其子类,那么自定义异常是受检异常
- 如果继承的是RuntimeException或其子类,那么自定义异常是未受检异常
异常处理
Java语言对异常处理的支持包括:
- catch
- throw
- finally
- try-with-resources
- throws
catch匹配
- 异常处理机制将根据抛出的异常类型找第一个匹配的catch块,找到后,执行catchk块内的岱庙,不再执行其他catchk块,如果没有找到,会继续到上层方法中查找。
- 注:抛出的异常类型是catch种声明异常的子类也算匹配,所以需要将最具体的子类放在前面。
- e.getMessage() //获取异常消息
- e.printStackTrace() //打印异常栈到标准错误输出流
- Java7的新语法,多个异常之间可以用"|"操作符
重新抛出异常
- catch块内处理完后,可以重新抛出异常,异常可以是原来的,也可以是新建的。
- 为什么重新抛出异常:当前代码不能够完全处理该异常,需要调用者进一步处理。
- 为什么抛出新的异常:当前异常不合适,信息不够,需要补充信息;过于细节,不便于调用者理解和使用。
finally
- finally内的代码不管有无异常发生,都会执行
- 如果没有异常发生,再try内的代码执行结束后执行
- 如果有异常发生且被catch捕获,再catch内的代码执行结束后执行。
- 如果有异常发生但没被捕获,则在异常被抛给上层之前执行。
- finally一般用于释放资源,如数据库连接、文件流等。
注:return和finally
- 在tryh或者catch语句中有return语句,在finally执行结束后执行,但finally并不能改变返回值。
- 这个函数的返回值是0,而不是2。实际执行的过程:在执行到try内的return ret;语句前,会先将返回值ret保存在一个临时变量中,然后才执行finally语句,最后try再返回那个临时变量,finally对ret的修改不会被改变。
public static int test(){
int ret = 0;
try{
return ret;
}finally{
ret = 2;
}
}
- 在finally中也有return语句。try和catch内的return会丢失,实际会返回finally中的返回值。finally中return不仅会覆盖try和catch内的返回值,还会掩盖try和catch内的异常,就像没有异常发生一样。
- 5/0会触发ArithmeticExceptiion,但finally中有return语句,这个方法就会返回2,而不是向上传递异常了。
public static int test(){
int ret = 0;
try{
int a = 5/0
return ret;
}finally{
ret = 2;
}
}
- finally中如果抛出异常,原异常也会被覆盖。
public static int test(){
int ret = 0;
try{
int a = 5/0
return ret;
}finally{
throw new RuntimeException("hello");
}
}
try-with-resources
对于资源的使用,如文件和数据库连接,典型的使用流程是首先要打开资源,最后在finally语句中调用资源关闭的方法,
针对这种场景,Java7开始支持一种新的语法,称之为try-with-resources,这种语法针对实现了javalang.AutoCloseable接口的对象。
public interface AutoCloseable{
void close() throws Exception;
}
没有try-with-resource时:
public static void useResource() throws Exception{
AutoCloseable r = new FileInputStream("hello");//创建资源
try{
//使用资源
}finally{
r.close();
}
}
使用try-with-resource时:
public static viod useResource() throws Exception{
try(AutoCloseable r = new FileInputStream("hello")){//创建资源
//使用资源
}
}
- 资源声明的语句和初始化放在try语句内,不用再调用finally,在语句执行完try语句后会自动调用资源的close方法,资源可以定义多个,以分号分隔
- Java 9之前,资源必须声明和初始化在try语句块内
- Java 9,去除了这个限制,资源可以在try语句外被声明和初始化,但必须是final的或者是事实上final的(虽然没有声明为final,但也没有被重新赋值)
throws
- throws用于声明一个方法可能抛出的异常。
- throws跟在方法的括号后面, 可以声明多个异常,以逗号分隔
- 这个声明的含义:这个方法内可能跑出这些异常,且没有对这些异常进行处理,至少没有处理完,调用者必须进行处理。
- 对未受检异常,不要求使用throws进行声明的。
- 对于受检异常,必须进行声明。不可以抛出而不声明,但可以声明抛出而不抛出。
- 主要用于父类方法中的声明,父类方法内可能没有抛出,但子类重写方法后可能就抛出了,子类不能抛出父类方法中没有声明的异常,所以就将所有可能的抛出的异常就都写到父类上了。
public static viod useResource() throws AppException,SQLException, NumberFormatException{
//主体代码
}
受检异常和未受检异常
- 受检异常必须出现在throws语句中
- 未受检异常表示程序的逻辑错误。【程序员应该解决代码bug而不是想办法处理异常】
- 受检异常表示程序本身没问题,但 I/O、网络、数据库等其他不可预测的错误导致的。【调用者应当进行适当处理】