8.1 异常概述
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据。
- 要打开的文件不存在。
- 网络通信时连接中断,或者JVM内存溢出
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
例8.1
package eight;
public class Baulk { // 创建类Baulk
public static void main(String[] args) { // 主方法
int result = 3 / 0; // 定义int型变量并赋值
System.out.println(result); // 将变量输出
}
}
8.2 异常分类
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
8.2.1 系统错误 ————Error
Error类及其子类通常用来描述Java运行系统中的内部错误,该类定义了常规环境下不希望由程序捕获的异常
public static void main(String[] args) {
System.out.println("梦想照亮现实!!!")//此化处缺少必要的分号
}
8.2.2 异常——Exception
运行时异常
编译时不会报错,但程序运行起来如果有错误就会报异常。
运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
常见的运行时异常
例8.2
package eight;
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 以及用户自定义的异常等。
常见的非运行时异常:
例8.3
package eight;
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();*/System.out.println("无信息");
}
}
public static void main(String[] args) {
FootballTeam team = new FootballTeam();
team.teamName = "com.mrsoft";
team.playerNum = 19;
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 语句块后面的第一条语句开始执行。
package eight;
public class Take {// 创建类
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(); // 输出异常性质
}
System.out.println("program over"); // 输出信息
}
}
2.finally 代码块
finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
public class Take { //创建类
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"); //输出信息
}
}
}
8.4 在方法中抛出异常
1.使用 throws 关键字抛出异常
throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常,多个异常可使用逗号分隔,使用throws关键字抛出异常的语法格式为:
返回值类型名 方法名(参数表) throws 异常类型名 {
方法体
}
使用throws为方法抛出异常时,如果子类继承父类,子类重写方法抛出的异常也要和原父类方法抛出的异常相同或是其异常的子类,除非throws异常时RuntimeException。
package eight;
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 eight;
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类:
public class MyException extends Exception { //创建自定义异常,继承Exception类
public MyException(String ErrorMessage) { //构造方法
super(ErrorMessage); //父类构造方法
}
}
创建类Tran,使用throw关键字抛出自定义类异常对象:
package eight;
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)如果父类抛出多个异常,则覆盖方法必须抛出相同的异常或其异常的子类,不能抛出新异常