8.1 异常概述
在 Java 语言中,将程序执行中发生的不正常情况称为“ 异常 ”。 (开发过程中的语法错误和逻辑错误不是异常)
package eight1;
public class Baulk {
public static void main(String[] args) {
// TODO Auto-generated method stub
int result = 3 / 0; // 定义int型变量并赋值
System.out.println(result); // 将变量输出
}
}
运行结果如下:
8.2 异常的分类
1.系统错误——Error
Error 类及其子类通常用来描述 Java 运行系统中的内部错计吴,该类定义了常规环境下不希望由程序捕获的异常,比如OutOfMemoryError、ThreadDeath等,这些错误发生时,Java虚拟机(JVM)一般会选择线程终止。
例如,下面的代码在控制台中输出“梦想照亮现实”这句话活,代码如下:
public static void main(String[] args) {
System.out.println("梦想照亮现实!!!")//此化处缺少必要的分号
}
2.异常——Exception
运行时异常
编译时不会报错,但程序运行起来如果有错误就会报异常。
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
常见的运行时异常:
异常类 | 说明 |
ClassCastException | 类型转换异常,当把一个对象归为某个类,但实际上此对象并不是由这个类创建的,也不是其子类创建的,则会引起异常 |
NullPointerException | 空指针异常,程序试图访问一个空的数组中的元素或访问空的对象中的方法或变量时产生异常 |
ArryIndexOutOfBoundsException | 索引越界异常,由于数组下标越界或字符串访问越界引起异常 |
ArithmeticException | 算数运算异常,由于除数为0引起的异常 |
ArrayStoreException | 数组中包含不兼容的值抛出的异常(由于数组存储空间不够引起的异常) |
NumberFormatException | 字符串转换为数字抛出异常 |
IllegalArgumentException | 非法参数异常 |
FileSystemNotFoundException | 文件系统未找到异常 |
SecurityException | 安全性异常 |
StringIndexOutOfBoundsException | 字符串索引超出范围抛出的异常 |
NegativeArrySizeException | 数组长度为负异常 |
注:
ConcurrentModificationException 并发修改异常;
NoSuchElementException 找不到元素异常;
UnsupportedOperationException 不支持请求异常;(使用Arrays工具类的asList将数组转成集合增加元素时,会报此异常)
package eight2;
public class Thundering { // 创建类
public static void main(String[] args) { // 主方法
String str = "lili"; // 定义字符串
System.out.println(str + "年龄是:"); // 输出的提示信息
int age = Integer.parseInt("20L"); // 数据类型的转换
System.out.println(age); // 输出信息
}
}
运行结果如下:
非运行时异常
非运行时异常是RuntimeException类及其子类异常以外的异常,我们必需对出现的这些异常进行处理,否则程序就不能编译通过。如 IOException、SQLException 以及用户自定义的异常等。
常见的非运行时异常:
异常类 | 说明 |
ClassNotFoundException | 未找到相应异常 |
SQLException | 操作数据库异常 |
IOException | 输入/输出流异常 |
TimeoutException | 操作超时异常 |
FileNotFoundException | 文件未找到异常 |
注:
反射操作异常:ReflectiveOperationException
方法未找到异常:NoSuchMethodException
字段未找到异常:NoSuchFieldException
非法访问权限异常:IllegalAccessException
实例化异常:InstantiationException
不支持克隆异常:CloneNotSupportedException
被中止异常:InterruptedException
package eight3;
public class FootballTeam {
private int playerNum; // 定义“球员数量”
private String teamName; // 定义“球队名称”
public FootballTeam() // 构造方法FootballTeam()
{
// 寻找“教练”类
try {
Class.forName("com.mrsoft.Coach");
} catch (ClassNotFoundException e) { //catch代码块用来获取异常信息
// TODO Auto-generated catch block
e.printStackTrace(); //输出报错信息
}
}
public static void main(String[] args) {
FootballTeam team = new FootballTeam(); // 创建对象team
team.teamName = "com.mrsoft"; // 初始化teamName
team.playerNum = 19; // 初始化playerNum
System.out.println("\n球队名称:" + team.teamName + "\n" + "球员数量:" + team.playerNum + "名");
}
}
运行结果如下:
8.3 捕捉处理异常
try..catch 代码块主要用来对异常进行捕捉并处理。在实际使用时,该代码块还有一个可选的 finally代码块,其标准语法如下:
try{
//程序代码块
}
catch (Exceptiontype e) {
//对Exceptiontype的处理
}
finally{
//代码块
}
其中,try代码块中是可能发生异常的Java代码; catch 代码块在try代码块之后,用来激发被
捕获的异常;finally代码块是异常处理结构的最后执行行部分,无论程序是否发生异常,finally 代码块中的代码都将执行,因此,在 finally 代码块中通常放置一些释放资源、关闭对象的代码。
1.try...catch 代码块
如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。
2.finally 代码块
finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
8.4 在方法中抛出异常
1.使用 throws 关键字抛出异常
throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常,多个异常可使用逗号分隔,使用throws关键字抛出异常的语法格式为:
返回值类型名 方法名(参数表) throws 异常类型名 {
方法体
}
注:使用throws为方法抛出异常时,如果子类继承父类,子类重写方法抛出的异常也要和原父类方法抛出的异常相同或是其异常的子类,除非throws异常时RuntimeException。
package eight6;
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关键字将异常抛给上一级后,不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的代码。
2.使用 throw 关键字抛出异常
throw关键字通常用于在方法体中"制造"一个异常,程序在执行到 throw 语句时立即终止,它后面的语句都不执行。使用throw关键字抛出异常的语法格式为:
throw new 异常类型名(异常信息)
throw通常用于在程序中出现某种逻辑错误时,由开发者主动抛出某种特定类型的异常。
例如:
package eight7;
public class ThrowTest {
public static void main(String[] args) { // 主方法
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);
}
}
运行结果如下:
注:throw通常用来抛出用户自定义异常,通过throw关键字抛出异常后,如果想在上一级代码中捕获并处理异常,最好在抛出异常的方法声明中使用throws关键字指明要抛出的异常,如果要捕捉throw抛出的异常,则需要使用try…catch代码块。
8.5 自定义异常
自定义异常类的使用步骤如下:
1)自定义异常类继承Exception类;
2)在要抛出异常的函数使用throws关键字;
3)使用 try...catch 代码块捕获并处理异常;
4)再出现异常方法的调用者中捕获并处理异常。
创建自定义异常类,该类继承Exception类:
package eight8;
public class MyException extends Exception { //创建自定义异常,继承Exception类
public MyException(String ErrorMessage) { //构造方法
super(ErrorMessage); //父类构造方法
}
}
创建类Tran,使用throw关键字抛出自定义类异常对象:
package eight8;
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) { // 主方法
try { // try代码块处理可能出现异常的代码
avg(-50);
} catch (MyException e) {
e.printStackTrace();
}
}
}
运行结果如下:
8.6 异常的使用原则
Java异常强制用户去考虑程序的强健性和安全性。异常处理不应该用来控制程序的正常流程,其主要作用是捕获程序在运行时发生的异常并进行相应的处理。 编写代码处理某个方法可能出现的异常时,可遵循以下原则。
1)不要过度使用异常。虽然通过异常可以增强程序的健壮性,但如果使用过多不必要的异常处理,可能会影响程序的执行效率。
2)不要使用过于庞大的 try….catch 块。在一个try块中放置大量的代码,这种写法看上去“很简单”,但是由于try块中的代码过于庞大,业务过于复杂,会造成try块中出现异常的可能性大大增加,从而导致分析异常原因的难度也大大增加。
3)避免使用 catch(Exception e)。因为如果所有异常都采用相同的处理方式,将导致无法对不同异常分情况处理;另外,这种捕获方式可能将程序中的全部错误、异常捕获到,这时如果出现一些“关键”异常,可能会被“悄悄地”忽略掉。
4)不要忽略捕捉到的异常,遇到异常一定要及时处理。
5)如果父类抛出多个异常,则覆盖方法必须抛出相同的异常或其异常的子类,不能抛出新异常。