java异常处理
异常(Exception)的概念
异常指程序运行过程中出现的非正常现象,例如用户输入错误、除数为零、需要处理的文件不存在、数组下标越界等。
传统的异常处理方式多采用标识程序出现的异常情况。如使用if分支来处理可能发生的异常。这会有多个坏处:
1. 逻辑代码和错误处理代码放一起!
2. 程序员本身需要考虑的例外情况较复杂,对程序员本身要求较高!
在Java的异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。
所谓异常处理,就是指程序在出现问题时依然可以正确的执行完。
Java是采用面向对象的方式来处理异常的。处理过程:
1. 抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。
2. 捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
异常分类
JDK 中定义了很多异常类,这些类对应了各种各样可能出现的异常事件,所有异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception。
Error
Error是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Error表明系统JVM已经处于不可恢复的崩溃状态中。我们不需要管它。
Exception
Exception是程序本身能够处理的异常,如:空指针异常(NullPointerException)、数组下标越界异常(ArrayIndexOutOfBoundsException)、类型转换异常(ClassCastException)、算术异常(ArithmeticException)等。
Exception类是所有异常类的父类,其子类对应了各种各样可能出现的异常事件。 通常Java的异常可分为:
1. RuntimeException 运行时异常
2. CheckedException 已检查异常
Error与Exception的区别
1. 我开着车走在路上,一头猪冲在路中间,我刹车。这叫一个异常。
2. 我开着车在路上,发动机坏了,我停车,这叫错误。系统处于不可恢复的崩溃状态。发动机什么时候坏?我们普通司机能管吗?不能。发动机什么时候坏是汽车厂发动机制造商的事。
异常处理的方式:try-catch-finally
异常处理的方式有两种:使用“try-catch-finally”捕获异常、使用“throws”。
语法:
try{
可能出现异常的代码片段
}catch(Exception e){//一般最后一个catch捕获的是Exception 这样可以避免因为未捕获的异常而导致程序中断
当try中出现XXXException后的解决办法
}finally{
无论是否捕获异常里面代码都将会执行
}
finally块
finally块是异常处理机制的最后一块 他可以直接跟在try语句块之后(相对少) 或者最后一个catch块之后。
finally确保只要程序进行try块中,那么finally块中的代码必定执行(无论try-catch仲有无return)。
所以通常我们将无论程序是否出现异常,都要继续去执行的代码放在finally块中。 如:IO操作后的关闭流。
注:finally中一般不会去写return 因为只要finally有return 前面的所有return语句全部没用。
自动关闭特性
JDK7之后推出的 使得我们在IO操作中的异常处理写法更加简洁。编译器最终会在编译时将代码改为传统的try-catch-finally的形式,并在finally中关闭这里定义的流
例 try(FileOutputStream fos=new FileOutputStream("fos.txt"){
。。。。。。。
}catch{......}
注:try小括号中只能写流 (只能写实现了Autocloseble类的 ,所有的流都实现了这个类)。
异常处理的方式: throws
通常遇到以下两种情况是我们会主动向外抛出异常
1. 当遇到满足语法要求但是不符合业务逻辑要求时主动向外抛出异常。
2. 程序确实出现了异常,但是异常不应该在当前代码片段中被解决。
语法:
使用throws关键字在方法头部抛出异常。如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常之间以逗号隔开。
public static void readFile(String fileName)throws IOException {
}
必要检查异常、非必要检查异常
如果所有异常都是必要检查异常 则异常检查就成了代码必要的部分 会使代码非常的啰嗦。
只有 RuntimeException及其子类属于常识性异常 无需检查异常 、像空指针异常、数组越界都是 无需做异常检查自己注意就行。
自定义异常类
1.在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类。
2.自定义异常类只需从Exception类或者它的子类派生一个子类即可。
3.自定义异常类如果继承Exception类,则为受检查异常,必须对其进行处理,如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。
使用自定义异常类的步骤
1.创建一个自定义的异常类,如IllegalAgeException,使其继承Exception,并设置序列号版本serialVersionUID。
2.利用eclipse中的Source->Generate Constructors fromSuperclass 来继承父类Exception的构造方法。
3.在可能抛出此异常的方法首部添加throws关键字抛出自定义异常类,在此方法内部添加throw new IllegalAgeException("*xxxx") 使得如果调用此方法不满足特定条件时打印错误信息。如果是throw new RuntimeException("xxx") 则方法首部可以不用添加throws关键字来抛出此异常。例如:
//IllegalAgeException为自定义异常
public void setAge(int age) throws IllegalAgeException { if (age < 0) { throw new IllegalAgeException("人的年龄不应该为负数"); } this.age = age; }
注:这时如果在调用此方法则需要对此进行异常处理,使用try-catch或throws向上一级抛出。
//若抛出的是RuntimeException或是其子类则无需再方法首部添加throws
public void setAge(int age) {
if (age < 0) {
throw new RuntimeException("人的年龄不应该为负数");
}
this.age = age;
}
注:此时若调用此方法无需对此进行异常处理。
throws 和 throw
throw是用来抛出异常的 ,throws是用来嘱咐编译器当调用这个方法时要进行异常处理。
1.throws可以理解为时丑话说在前面,就是我明确告诉你调用我这个方法可能会报这个错误,调用它时你必须要处理这个异常当。某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理。(处理时该我们自己管 用try-catch ,不该我们管用throws声明该异常的抛出。)
2.throw是抛出一个异常的实例,总是出现在函数体中。程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
3.一般情况要在方法声明后面写throws,告诉调用者调用这个方法时有可能要抛出什么异常 必须要进行处理。实际上java除了RuntimeException及其子类型异常时不用throws, 其他异常必须要在方法声明后面加throws xxx。
4. throws可以单独使用,但throw不能;throw要么和try-catch-finally语句配套使用,要么与throws配套使用。但throws可以单独使用,然后再由处理异常的方法捕获。
5.一定不要在main函数写throws 。
子类在重写超类中含有throws声明异常抛出的方法时的重写规则
可以是和父类一样的形式去重写 即父类抛出多少异常子类也抛出多少
允许不抛出任何异常
允许仅抛出部分异常
允许抛出父类方法抛出异常的子类型异常
不允许抛出额外异常
不允许抛出父类抛出异常的父类型异常
异常常用的方法
void printStackTrace() //打印错误堆栈信息,助于定位错误并改正。调试错误信息时,看at后面是自己的包。
String getMessage() //错误信息的描述