目录
8.1 异常概述
在程序中,错误可能产生于程序员没有预料到的各种情况,或者是超出了程序员可控范围的环境因素,如试图打开一个根本不存在的文件等,在Java中,这种在程序运行时可能出现的一些错误称为异常.
使用这种处理机制,使得Java语言的异常处理更加灵活,Java语言编写的项目更加稳定.当然,异常处理机制也存在一些弊端,例如,使用异常处理可能会降低程序的执行效率,增加语法复杂度等.
public class Baulk { //创建类Baulk
public static void main (String[] args) { //主方法
int result = 3/0; //定义int 型变量并赋值
System.out.println(result); //将变量输出
}
}
程序运行结果报告发生了算术异常ArithmeticException(根据给出的错误提示可知发生错误是因为在算术表达式"3/0"中,0作为除数出现),系统不再执行下去,提前结束,这种情况就是所说的异常.
8.2 异常的分类
Java类库的每一个包中都定义了异常类,所有这些类都是Throwable类的子类.Throwable类派生了两个子类,分别是Error类和Exception类,其中,Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重.Exception类为非致命性类,可以通过捕捉处理使程序继续执行.Exception类又可以根据错误发生的原因分为运行时异常和非运行时异常.
8.2.1 系统错误---Error
Error类及其子类用来描述Java运行系统中的内部错误,该类定义了常规环境下不i希望由程序捕获的异常,比如OutOfMemoryError,ThreadDeath等,这些错误发生时,Java虚拟机(JVM)一般会选择线程终止.例如以下代码:
public static void main(String[] args) {
System.out.println("梦想照亮现实!!!") //此处缺少必要的分号
}
从代码结果可以看到的异常信息为"java.lang.Error",说明这是一个系统错误,程序遇到这种错误,通常都会停止执行,而且这类错误无法使用处理语句出路.
8.2.2 异常---Exception
Exception 是程序本身可以处理的异常,这种异常主要分为运行时异常和非运行时异常,程序中应当可能去处理这些异常.
1.运行时异常
运行时异常是程序运行过程中产生的异常,它是RuntimeException类及其子类异常,如NullPointerException,IndexOutOfBoundException等,这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生.
这些异常可通过try....catch语句捕获,如下表所示:
例如:将一个字符串转化为整型,可以通过Integer类的parseInt()方法来实现.如果该字符串不是数字形式,parseInt()方法就会显示异常,程序将在出现异常的位置终止,不再执行下面的语句.
public class Thundering {//创建类
public static void main(String[] args) {//主方法
// TODO Auto-generated method stub
String str = "lili";//定义字符串
System.out.println(str + "年龄是:");//输出的提示信息
int age = Integer.parseInt("20L");//数据类型的转换
System.out.println(age);//输出信息
}
}
本实例报出的是NumberFormatException异常,该异常实质是由于开发人员的逻辑错误造成的.
2.非运行时异常
非运行时异常时RuntimeException类及其子类异常以外的异常.从程序语法角度讲,这类异常是必须进行处理的异常,如不处理,程序就不能编译通过,如IQException,SQLException以及用户自定义的异常.如下表所示:
public class FootballTeam {
private int playerNum;
private String teamName;
public FootballTeam()
{
//寻找教练类
Class.forName("com.mrsoft.Coach");
}
public static void main(String[] args) throws ClassNotFoundException {
FootballTeam team = new FootballTeam();
team.teamName ="com.mrsoft";
team.playerNum = 19;
System.out.println("\n球队名称:" +team.teamName+"\n"+"球员数量:"+ team.playerNum + "名");
// TODO Auto-generated method stub
}
}
使用try...catch代码块进行处理其结果如下
public class FootballTeam {
private int playerNum;
private String teamName;
public FootballTeam()
{
try {
Class.forName("com.mrsoft.Coach");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
//
System.out.print("未找到");
e.printStackTrace();
}
}
public static void main(String[] args) throws ClassNotFoundException {
FootballTeam team = new FootballTeam();
team.teamName ="com.mrsoft";
team.playerNum = 19;
System.out.println("\n球队名称:" +team.teamName+"\n"+"球员数量:"+ team.playerNum + "名");
// TODO Auto-generated method stub
}
}
8.3 捕捉处理异常
前面讲解非运行异常时,提到了系统会自动为非运行时异常提供两种解决方案,一种是使用throws关键字,一种是使用try...catch代码块,这两种方法都是用来异常进行处理的.
try.....catch代码块主要用来对异常进行捕捉处理.在实际使用时,该代码块还有一个可选的finally代码块,如下:
try {
//程序代码块
}
catch(Exception e){
//对Exceptiontype的处理
}
finally {
//代码块
}
其中try代码块中是可能发生异常的Java代码;catch代码块在try代码块之后,用来激发被捕获的异常;finally代码块是异常处理结构的最后执行部分,无论程序是否发生异常,finally代码块中的代码都将执行,因此,在finally代码块中通常放置一些释放资源,关闭对象的代码.
8.2.2 try.....catch代码块
使用该代码块对其进行异常处理:
public class Thundering {//创建类
public static void main(String[] args) {//主方法
try {
// TODO Auto-generated method stub
String str = "lili";//定义字符串
System.out.println(str + "年龄是:");//输出的提示信息
int age = Integer.parseInt("20L");//数据类型的转换
System.out.println(age);//输出信息
}catch (Exception e) {
e.printStackTrace();
}
System.out.println("program over");
}
}
上面代码中,在catch代码块中使用了Exception对象的printStackTrace()方法输出异常的栈日志,除此之外,Exception对象还提供了其他的方法用于获取异常的相关信息,最常用的3个方法如下:
(1) getMessage()方法:获取有关异常事件的信息.
(2) toString()方法:获取异常的类型与性质
(3) printStackTrace()方法:获取异常事件发生执行堆栈的内容.
8.3.2 finally代码块
完整的异常处理语句应该包含finally代码块,通常情况下,无论程序中有无异常发生,finally代码块中的代码都可以正常执行.
public class Thundering {//创建类
public static void main(String[] args) {//主方法
try {
// TODO Auto-generated method stub
String str = "lili";//定义字符串
System.out.println(str + "年龄是:");//输出的提示信息
int age = Integer.parseInt("20L");//数据类型的转换
System.out.println(age);//输出信息
}catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("program over");
}
}
}
从上图结果可以看出,程序在捕捉完异常信息之后,会执行finally代码中的代码,另外,在以下3种情况下,finally块不会被执行;
1.在finally代码块中发生了异常;
2.在前面的代码中使用了System.exit()退出程序;
3.程序所在的线程死亡.
8.4 在方法中抛出异常
如果某个方法可能会发生异常,但不想在当前方法处理这个异常,则可以使用throws,throw关键字在方法中抛出异常.
8.4.1 使用throws关键字抛出异常
throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常,多个异常可使用逗号分隔,其语法格式为:
返回值类型名 方法名 (参数表) throws 异常类型名 {
方法体
}
public class Shoot {
static void pop() throws NegativeArraySizeException {
//定义方法并抛出NegativeArraySizeException 异常
int[] arr = new int[-3]; //创建数组
}
public static void main (String[] args) { //主方法
try { //try语句处理异常信息
pop(); //调用pop()方法
} catch(NegativeArraySizeException e) {
System.out.println("pop()方法抛出异常"); //输出异常信息
}
}
注:使用throws为方法抛出异常时,如果子类继承父类,子类重写方法抛出的异常也要和原父类方法抛出的异常相同或是其异常子类,出发throws异常是 RuntimeException.
8.4.2 使用throw关键字抛出异常
throw关键字通常用于方法中"制造"一个异常,程序在执行到throw语句时立即终止,他后面的语句都不会执行.
throw 通常用来抛出用户自定义异常,通过 throw 关键字抛出异常后,如果想在上一级代码中捕获并处理异常最好在抛出异常的方法声明中使用 throws 关键字指明要抛出的异常;如果要捕捉 throw 抛出的异常,则需要使用try...catch代码块。
throws 关键字和 throw 关键字的区别如下。
(1) throws 用在方法声明后面,表示抛出异常,由方法的调用者处理,而 throw用在方法体内,用来制造一个异常,由方法体内的语句处理。
(2) throws 是声明这个方法会抛出这种类型的异常,以便使它的调用者知道要捕获这个异常,而 throw是直接抛出一个异常实例。
(3) throws表示出现异常的一种可能性,并不一定会发生这些异常,如果使用throw,就一定会产生某种异常。
8.5 自定义异常
使用Java内置的异常类可以描述在编程时出现的大部分异常情况,但有写情况时通过内置异常类无法识别的,如下面代码:
int age = -50;
System.out.println("王师傅今年 "+age+" 岁了!");
上面代码运行时没有任何问题,但是大家想一想:人的年龄可能是负数吗?这类问题编译器是无法识别的,但很明显不符合常理,那么,对于这类问题即可通过自定义异常对它们进行处理。Java中可以通过继承Exception 类自定义异常类。
在程序中使用自定义异常类,大体可分为以下几个步骤。
(1)创建自定义异常类。
(2)在方法中通过 throw 关键字抛出异常对象。
(3)如果在当前抛出异常的方法中处理异常,可!以使用try….catch代码块捕获并处理,否则,在方法的声明处通过throws 关键字指明要抛给方法调用者的异常,继续进行下一步操作。
(4)在出现异常方法的调用者中捕获并处理异常.
自定义异常主要用在以下场合。
(1)使异常信息更加具体,比如跟别人合作开发时,程序出现了空指针异常,但别人可能不清楚这个空指针是如何产生的,这时即可自定义一个显显示具体信息的异常, 比如自定义一个
用户信息为空时抛出的异常:NullOfUserInfoException, 当这个异常发生就代表用户填写的信息
不完整。
(2)程序中有些错误是符合 Java 语法的,但不符合业业务逻辑或者实际情况,比如程序中出现了一个人的年龄是负数、人员个数为小数等。
(3)在分层的软件架构中,通常在表现层统一对系统其他层次的异常进行捕获处理。
8.6 异常的使用原则
异常处理不应该用来控制程序的正常流程
Java异常强制用户去考虑程序的强健性和安全性。
其主要作用是捕获程序在运行时发生的异常并进行相应 的处理。编写代码处理某个方法可能出现的
异常时,可遵循以下原则。
(1)不要过度使用异常。虽然通过异常可以增强和程序的健壮性,但如果使用过多不必要的异常处理,可能会影响程序的执行效率。
(2)不要使用过于庞大的 try….catch 块。在一个 try 块中放置大量的代码,这种写法看上去“很
简单”,但是由于 try 块中的代码过于庞大,业务过于复杂 会造成try块中出现异常的可能性大大
增加,从而导致分析异常原因的难度也大大增加。
(3)避免使用catch(Exceptione)。因为如果所有异常都都采用相同的处理方式,将导致无法对不同异常分情况处理;另外,这种捕获方式可能将程序中的?全部错误、异常捕获到,这时如果出现一些“关键”异常,可能会被“悄悄地”忽略掉。
(4)不要忽略捕捉到的异常,遇到异常一定要及时处理。
(5)如果父类抛出多个异常,则覆盖方法必须抛出材目同的异常或其异常的子类,不能抛出新异常。