文章目录
Java SE
包括Exception类、try语句、catch语句、throw语句、throws声明、finally块、RuntimeException、多个catch块、重写方法的异常处理等。
在Java语言出现之前,传统的异常处理方式多采用返回值来标识程序出现的异常情况,这种方式虽然为程序员所熟悉,但却有多个坏处
(1)、首先,一个API可以返回任意的返回值,而这些返回值本身不能解释该返回值是否代表一个异常情况发生了和该异常的具体情况,需要调用API的程序自己判断并解释返回值的含义
(2)、其次,并没有一种机制来保证异常情况一定会得到处理,调用程序可以简单的忽略该返回值,需要调用API的程序员记住去检测返回值并处理异常情况。这种方式还让程序代码变得冗长,尤其是当进行IO操作等容易出现异常情况的处理时,代码的很大部分用于处理异常情况的switch分支,程序代码的可读性变得很差
1、java异常处理机制
当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,java虚拟机检测寻找和try关键字匹配的处理该异常的catch块,如果找到,将控制权交到catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。如果没有找到处理该异常的catch块,在所有的finally块代码被执行和当前线程的所属的ThreadGroup的uncaughtException方法被调用后,遇到异常的当前线程被中止
2、Exception类
Java异常结构中定义有Throwable类,Exception和Error是其派生的两个子类
Exception表示由于网络故障、文件损坏、设备错误、用户输入非法等情况导致的异常
Error表示Java运行时环境出现的错误,例如:JVM内存资源耗尽等
异常处理的目的就是当异常发生时妥善的终止程序,避免灾难性后果的发生,具体的操作通常包括:
-
通知:向用户通知异常的发生
-
恢复:保存重要的数据、关闭文件、回滚事务等
-
退出:以恰当的形式退出程序
/**
* Exception常用方法
*/
public class ExceptionDemo6 {
public static void main(String[] args) {
System.out.println("程序开始!");
try{
String str = "abc";
System.out.println(Integer.parseInt(str));
}catch (Exception e){
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("程序结束!");
}
}
3、try语句
try {…}语言指定一段代码,该段代码就是一次捕获并处理异常的范围
在执行过程中,该段代码可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句分别对这些异常做相应的处理
如果没有异常产生,所有的catch代码段都被略过不执行
4、catch语句
在catch语句块中是对异常进行处理的代码,每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常
在catch中声明的异常对(catch(SomeException e))封装了异常事件发生的信息,在catch语句块中可以使用这个对象的一些方法获取这些信息。例如:
-
getMessage()方法,用来得到有关异常事件的信息
-
printStackTrace()方法,用来跟踪异常事件发生的执行推栈的内容
/**
* java异常捕获机制中的try-catch
* try块是用来扩上可能出错的代码片段
* catch块是用来捕获try块中代码抛出的错误,并解决
*/
public class ExceptionDemo1 {
public static void main(String[] args) {
System.out.println("程序开始运行");
try{
String str = "a";
/*
JVM在执行代码的过程中若发现了一个异常,则会实例化这种情况的异常实例
并将代码整个执行过程完整设置到异常中来表示该错误报告,设置完毕后将该异常抛出
若抛出异常的这句代码有被try包围,则JVM会检查catch中是否有关注该异常
关注则交给catch并解决,否则会将异常抛出到当前方法外,由调用当前方法的代码解决该异常
*/
System.out.println(str.length());
System.out.println(str.charAt(0)); //返回指定下标的字符
System.out.println(Integer.parseInt(str)); //将字符串转换为一个整数NumberFormatException
}catch (NullPointerException e){
System.out.println("空指针异常!");
}catch (StringIndexOutOfBoundsException e){
System.out.println("字符串下标越界异常!");
/*
应当养成一个好习惯,在最后一个catch中捕获Exception,避免因未捕获异常导致程序中断
当多个catch捕获不同异常时,这些异常间存在继承关系,那么子类异常要在上先行捕获,父类异常在下
*/
}catch (Exception e){
System.out.println("异常!");
}
System.out.println("程序结束运行");
}
}
**注:try块将一个或者多个语句放入try时,则表示这些语句可能抛出异常,编辑器知道可能要发生异常,于是用一个特殊的结构评估块内所有语句;catch块:当问题出现时,一种选择是自定义代码块来处理问题,catch块的目的便在于此,catch块是try块所产生异常的接受者**
5、throw语句
当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,在某些时刻,可能会想要自行抛出异常,例如在异常处理结束后,再将异常抛出,让下一层异常处理区块来捕捉,若想自行抛出异常,可以使用“throw”关键词,并生成指定的异常对象。例如:throw new ArithmenticException();
/**
* 用来测试throw与throws
*/
public class Person {
private int age;
/**
* 当一个方法中使用throw抛出一个异常时,就要在方法上使用throws声明该类异常的抛出以通知调用者解决
* 只有RuntimeException及其子类异常使用throw抛出时不强制要求必须使用throws声明
* 其他异常都是强制要求的,否则编译不通过
* @param age
* @throws Exception
*/
public void setAge(int age) throws Exception {
if(age<0||age>100){
throw new Exception("年龄不合法");
}
this.age = age;
}
public int getAge() {
return age;
}
}
**注:若要声明异常,则必须将其添加到方法签名块结束位置,这样,声明的异常将传给方法的调用者,而且也通知了编译器:该方法的任何调用者必须遵守处理或声明规则**
6、throws声明
程序中会定许多方法(Method),这些方法中可能会因某些错误而引发异常,但不希望直接在这个方法中处理这些异常,而希望调用这个它的方法来统一处理,可以使用“throws”关键词来声明这个方法将会抛出异常
/**
* 测试异常类的输出
*/
public class ExceptionDemo4 {
public static void main(String[] args) {
Person p = new Person();
/*
当调用一个含有throws声明异常抛出的方法时,编译器要求必须处理该异常
处理手段有两种:
1:使用try-catch捕获并处理
2:在当前方法上继续使用throws声明该异常的抛出(不能再main方法上使用throws,main方法时虚拟机调用,虚拟机处理异常直接kill)
*/
try {
p.setAge(200);
} catch (Exception e) {
}
System.out.println("年龄为:"+p.getAge());
}
}
7、finally块
(1)、finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序其它部分以前,能够对程序的状态作统一管理
(2)、无论try所指定的程序块中是否抛出异常,finally所指定的代码都要被执行
/**
* finally块
* finally块定义在异常捕获机制的最后,可以直接跟在try块之后或者最后一个catch块之后
* finally块中的代码一定执行,无论try块中的代码是否抛出异常,所以通常会把释放资源等操作放在finally中,例如关闭流
*/
public class ExceptionDemo2 {
public static void main(String[] args) {
System.out.println("程序开始!");
try{
String str = null;
}catch (Exception e){
System.out.println("执行异常!");
}finally {
System.out.println("finally!");
}
System.out.println("程序结束!");
}
}
(3)、通常在finally语句块中可以进行资源的消除工作,如关闭打开的文件、删除临时文件等
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
/**
* finally对于流的处理
*/
public class ExceptionDemo3 {
public static void main(String[] args) {
BufferedReader br = null;
try{
br = new BufferedReader(new InputStreamReader(new FileInputStream("src/day08/ExceptionDemo3.java")));
String line = null;
while ((line = br.readLine())!=null){
System.out.println(line);
}
//br.close(); //如果上面代码出现异常,会去catch中查找是否定义,catch执行完后会结束try的运行,br.close()当有异常时不会被执行到
}catch (Exception e){
System.out.println("程序执行异常!");
} finally {
if (br!=null){
try {
br.close();
}catch (Exception e){
}
}
}
}
}
(4)、try-catch-finally规则:
-
必须在try之后添加catch或finally块。try块可以同时接catch和finally块,但至少有一个块
-
必须遵守循环顺序:若代码同时使用catch和finally块,则必须将catch块放在try块之后
-
catch块与相应的异常类的类型有关
-
一个try可能有多个catch块。若如此,则执行第一个匹配块
-
可嵌套try-catch-finally结构
-
在tyr-catch-finally结构中可重新抛出异常
-
除了下列情况,总将执行finally做为结束:JVM过早终止;在finally块中抛出一个未处理的异常;计算机断电、失火、或遭遇病毒攻击
8、RuntimeException
Java异常可以分为可检测异常,非检测异常和自定义异常
可检测异常:可检测异常经常编译器验证,对于声明抛出异常的任何方法,编译器将强执行处理或声明规则,不捕捉这个异常,编译器就通不过,不允许编译
非检测异常:非检测异常不遵循处理或者声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器不会检查是否已解决了这样一个异常
自定义异常是为了表示应用程序的一些错误类型,为代码可能发生的一个或多个问题提供新含义
RuntimeException类也属于非检测异常,因为普通JVM操作引起的运行时异常随时可能发生,此类异常一般是由特定操作引发。但这些操作在java应用程序中会频繁出现。因此它们不受编译器检查与处理或声明规则的限制
9、常见RuntimeException
IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数
NullPointerException:当应用程序试图在需要对象的地方使用null时,抛出该异常
ArrayIndexOutOfBoundsException:当使用的数组下标超出数组允许范围时,抛出该异常
ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常
NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
10、多个catch块
catch代码块是异常处理代码。需要提供合理的处理,异常的处理是与具体业务逻辑有关。可以写多个catch处理一系列异常
注意:异常的大小关系,大类型的放到后面处理
11、重写方法时的异常处理
使用继承时,在父类别的某个方法上宣告了throws某些异常,而在子类别中重新定义该方法时
可以
-
不处理异常(重新定义时不设定throws)
-
可仅throws父类别中被重新定义的方法上之某些异常
-
可throws被重新定义的方法上之异常之子类别
不可以
-
throws出额外的异常
-
throws被重新定义的方法上之异常之父类别
import javax.xml.stream.FactoryConfigurationError;
import java.awt.*;
import java.io.IOException;
/**
* 重写父类一个含有throws异常抛出声明的方法时,子类该方法的throws的重写原则
*/
public class ExceptionDemo5 {
public void dosome() throws IOException, AWTException {
}
}
//不处理异常
class Aoo extends ExceptionDemo5 {
public void dosome() {
}
}
//抛出父类的部分异常
class Boo extends ExceptionDemo5 {
public void dosome() throws IOException {
}
}
//抛出父类异常的子类异常
class Coo extends ExceptionDemo5 {
public void dosome() throws FactoryConfigurationError {
}
}
/*
//不可以:抛出父类的额外异常
class Doo extends ExceptionDemo5 {
public void dosome() throws SQLException {
}
}
//不可以:抛出父类异常的父类
class Eoo extends ExceptionDemo5 {
public void dosome() throws Exception {
}
}
*/