目录
8.1 异常概述
在程序中,错误可能产生于程序员没有预料到的各种情况,或者是超出了程序员可控范围的环境因素,如试图打开一个根本不存在的文件等,在Java中,这种在程序运行时可能出现的一些错误称为异常。
创建Baulk类,再主方法中定义int型变量,将0作为除数赋值给该变量。
public class Baulk { //创建类Baulk
public static void main(String[] args) { //主方法
int result = 3 / 0; //定义int型变量并赋值
System.out.println(result); //将变量输出
}
}
运行的结果报告发生了算数异常ArithmeticException,系统不再执行下去,提前结束。
8.2 异常的分类
Java类库的每个包中都定义了异常类,所有这些类都是Throwable类。
Throwable类派生了Error和Exception连个子类。
Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重。
Exception称为非致命类,可以通过捕捉处理使程序继续执行。
8.2.1 系统错误 —— Error
Error类定义了常规环境下不希望由程序捕获的异常,比如:OutOfMemoryError、ThreadDeath等,这些错误发生时Java一般会选择终止。
public static void main(String[] args) { //主方法
System.out.println("梦想照亮现实!!!") //此处缺少必要的分号
}
8.2.2 异常 —— Exception
Exception是程序本身可以处理的异常,这种异常主要分为运行时异常和非运行时异常。
常见的运行时异常
异常类 说明
ClassCastExcrption 类型转换异常
NullPointerException 空指针异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ArithmeticException 算数异常
ArrayStoreException 数组包含不兼容的值抛出的异常
1. 运行时异常是指是程序运行过程中产生的异常,它是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等这类异常一般是由程序逻辑错误1引起的,应该从逻辑角度尽可能避免这类错误。
public class Baulk { //创建类Baulk
public static void main(String[] args) { //主方法
String str = "lili"; //定义字符串
System.out.println(str + "年龄是:"); //输出的提示信息
int age = Integer.parseInt("20L"); //数据类型的转换
System.out.println(age); //输出信息
}
}
2. 非运行时异常是RuntimeException类及其子类异常以外的异常,这类异常是必须进行处理的异常。如果不能处理,程序就不能编译通过,如:IOException、SQLException以及用户自定义的异常等。
常见的非运行时异常
异常类 说明
ClassNotFoundExcrption 未找到相应类异常
SOLExcrption 操作数据库异常类
IOException 输入/输出流异常
TimeoutException 操作超时异常
FileNotFoundException 文件为找到异常
public class FootballTeam {
private int playerNum; //定义球员数量
private String teamName; //定义“球员名称”
public FootballTeam()
{
//寻找教练类
class.forName("com.mrsoft.Coach");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
FootballTeam team = new FootballTeam();//创建对象team
team.teamName = "com.mrsoft";//初始化对象
team.playerNum = 19;//初始化对象
System.out.println("\n球队名称: " + team.teamName + "\n" + "球员数量: " + team.playerNum + "名");
}
单击编译器给的两种方案,代码会自动更正,如下:
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
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
FootballTeam team = new FootballTeam();//创建对象team
team.teamName = "com.mrsoft";//初始化对象
team.playerNum = 19;//初始化对象
System.out.println("\n球队名称: " + team.teamName + "\n" + "球员数量: " + team.playerNum + "名");
}
}
对于非运行时异常,必须使用try...catch代码块进行处理,或者使用throws关键字抛出
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
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
FootballTeam team = new FootballTeam();//创建对象team
team.teamName = "com.mrsoft";//初始化对象
team.playerNum = 19;//初始化对象
System.out.println("\n球队名称: " + team.teamName + "\n" + "球员数量: " + team.playerNum + "名");
}
}
对于非运行时异常,必须使用try...catch代码块进行处理,或者使用throws关键字抛出。
8.3 捕捉处理异常
在处理非运行时异常时,提到了系统会自动为非运行时异常提供两种解决方案,一种是使用throws关键字,一种是使用try...catch代码块。
try...catch代码块代码块主要用来对异常进行捕捉并处理。在实际使用时,改代码块还有一个可选的finally代码块。
try {
//程序代码块
}
catch(Exceptiontype e) {
//对Exceptiontype的处理
}
finally {
//代码块
}
8.3.1 try ... catch 代码块
public class Baulk { //创建类
public static void main(String[] args) { //主方法
try {
String str = "lili"; //定义字符串
System.out.println(str + "年龄是:"); //输出的提示信息
int age = Integer.parseInt("20L"); //数据类型的转换
System.out.println(age); //输出信息
} catch (Exception e) { //catch代码块用来获取异常信息
e.printStackTrace(); //输出异常性质
}
System.out.println("program over"); //输出信息
}
}
注意:Exception是try代码块传递给catch代码块的类型,e是对象名。
上面代码中,在catch代码块中使用了Exception对象的printStackTrace()方法输出了异常的栈日志,除此之外,Excrption对象还提供了其他的方法用于获取异常的相关信息,其最常用的3个方法如下:
(1)getMessage()方法:获取有关异常事件的信息。
(2)toString()方法:获取异常的类型与性质。
(3)printStackTrace()方法:获取异常事件发生时执行堆栈的内容。
在遇到需要处理多种异常信息的情况时,可以在一个try代码块后面跟多个catch代码块。
public class FootballTeam {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String str = "lili"; //定义字符串
System.out.println(str + "年龄是:"); //输出的提示信息
int age = Integer.parseInt("20L"); //数据类型的转换
System.out.println(age); //输出信息
} catch (NumberFormatException nfx) { //捕捉NumberFormatException异常
nfx.printStackTrace();
} catch (Exception e) { //Exception异常
e.printStackTrace(); //输出异常性质
}
System.out.println("program over"); //输出信息
}
}
这时将两个catch代码块的位置互换,代码块如下:
public class FootballTeam {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String str = "lili"; //定义字符串
System.out.println(str + "年龄是:"); //输出的提示信息
int age = Integer.parseInt("20L"); //数据类型的转换
System.out.println(age); //输出信息
} catch (Exception e) { //Exception异常
e.printStackTrace(); //输出异常性质
} catch (NumberFormatException nfx) { //捕捉NumberFormatException异常
nfx.printStackTrace();
}
System.out.println("program over"); //输出信息
}
}
这时编译器会出现以下错误提示,该错误就是由于多个catch代码块时,父异常类放在了子异常类的前面所引起的。Exception是所有异常类的父类。
8.3.2 finally 代码块
完整的异常处理语句应该包含finally代码块,通常情况下,无论程序中有无异常发生,finally代码块中的代码块都可以正常执行。
public class Baulk { //创建类
public static void main(String[] args) { //主方法
try { //try语句包含可能出现异常的程序代码
String str = "lili"; //定义字符串
System.out.println(str + "年龄是:"); //输出的提示信息
int age = Integer.parseInt("20L"); //数据类型的转换
System.out.println(age); //输出信息
} catch (Exception e) { //catch代码块用来获取异常信息
e.printStackTrace(); //输出异常性质
} finally
System.out.println("program over"); //输出信息
}
}
程序在捕捉完异常信息之后,会执行finally代码块中的代码。另外在以下3种情况下,finally块不会被执行:
(1)在finally代码块中发生了异常。
(2)在前面的代码中使用了S伊斯特曼。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) { //主方法
// TODO Auto-generated method stub
try { //try语句处理异常信息
pop(); //调用pop()方法
} catch (NegativeArraySizeException e) {
System.out.println("pop()方法抛出的异常"); //输出异常信息
}
}
}
注意:使用throws为方法抛出异常时,如果子类继承父类,子类重写方法抛出的异常也要和原父类方法抛出的异常相同或是其异常的子类,除非throws异常是RuntimeExcepption.
NullPointerException异常,代码修改如下:
try { //try语句处理异常信息
pop(); //调用pop()方法
} cacth ( NullPointerException e) {
System.out.println("pop()方法抛出的异常"); // 输出异常信息
}
运行程序,将会出现以下异常提示
因为Exception类是NegativeArraySizeException类的父类,这里相当于将异常交给了Exception处理。将代码修改如下:
try { //try语句处理异常信息
pop(); //调用pop()方法
} catch (Exception e) {
System.out.println("pop()方法抛出的异常"); //输出异常信息}
8.4.2 使用 throw 关键字抛出异常
throw关键字通常用于在方法体中“制作”一个异常,程序在执行到throw语句时立即终止,它后面的语句都不执行。
throw new 异常类型名(异常信息)
通常用于在程序出现逻辑错误1时,由开发者主动抛出某个特定类型的异常。
public class ThrowTest {
public static void main(String[] args) { //主方法
// TODO Auto-generated method stub
int num1 = 25;
int num2 = 0;
int result;
if (num2 == 0) //判断num2是否等于0,如果等于0,抛出异常
{
//抛出ArithmeticException异常
throw new ArithmeticException("这都不会,小学生都知道:除数不能是0!!!");
}
result = num1 / num2; //计算int1除以int2的值
System.out.println("两个数的商为: " + result);
}
}
throws和throw的区别:
(1)throws用在方法声明后面,表示抛出异常,由方法的调用者处理。而throw用在方法体内,用来制造一个异常,由方法体内的语句处理。
(2)throws是声明这个方法会抛出这种类型的异常,以便使它的调用者知道要捕获这个异常,而throw是直接抛出一个异常实例
(3)Theo我是表示出现异常的一种可能性,并不一定会发生这些异常,如果使用throw,就一定会产生某种异常。
8.5 自定义异常
int age = -50;
System.out.println("王师傅今年 "+age+" 岁了!");
程序中使用自定义异常类,大体可分为以下几个步骤。
(1)创建自定义异常类。
(2)在方法中通过throw关键字抛出异常对象。
(3)如果在当前抛出异常的方法中处理异常,可以使用try...catch代码块捕获并处理,否则,在方法的声明处通过throws关键字指明要抛给方法调用者的异常,继续进行下一步操作。
(4)在出现异常方法的调用者中捕获并处理异常。
public class MyException extends Exception { //创建自定义异常,继承Exception类
public MyException(String ErrorMessage) { //构造方法
super(ErrorMessage); //父类构造方法
}
}
public class Tran {
//定义方法,抛出自定义的异常
static void avg(int age) throws MyException {
if (age < 0) { //判断方法中参数是否满足指定条件
throw new MyException("年龄不可以使用负数"); //错误信息
} else {
System.out.println("王师傅今年 " + age + "岁了!"); //输出信息
}
}
public static void main(String[] args) { //主方法
// TODO Auto-generated method stub
try { //try代码块处理可能出现异常的代码
avg(-50);
} catch (MyException e) {
e.printStackTrace();
}
}
}
8.6 异常的使用原则
编写代码处理某个方法可能出现的异常时,可遵循以下原则:
(1)不要过度使用异常。虽然通过异常可以增强程序的健壮性,但如果使用过多不必要的异常处理,可能会影响程序的执行效率。
(2)不要使用过于庞大的try...catch块。在一个try块中放置大量的代码,这种写法看上去“很简单”,但是由于try块中的代码过于庞大,业务过于复杂,会造成try块中出现异常的可能性大大增加,从而导致分析异常原因的难度也大大增加。
(3)避免使用catch(Exception e)。因为如果所有异常都采用相同的处理方式,将无法对不同异常分情况处理;另外,这种捕获方式可能将程序中的全部错误、异常捕获到,这时如果出现一些“关键”异常,可能会被“悄悄地”忽略掉。
(4)不要忽略捕捉到的异常,遇到异常一定要及时处理。
(5)如果父类抛出多个异常,则覆盖方法必须抛出相同的异常或其异常的子类,不能抛出新异常。