什么是异常处理
假设在一个Java程序运行期间出现了一个错误,这个错误可能是由于文件包含了错误信息,或者网络连接出现问题造成的,也有可能是因为使用无效的数组下标,或者试图使用一个未赋值的对象引用而造成的。用户期望再出现错误时,程序能够采用一些理智的行为。如果由于出现错误而使得某些操作没有完成,程序应该:
- 返回到一种安全状态,并能够让用户执行一些其他的命令;或者
- 允许用户保存所有操作的结果,并以适当的方式终止程序
要做到这些并不简单,其原因时检测(或引发)错误条件的代码通常离那些能够让数据恢复到安全状态,或者能够保存用户的操作结果,并正常地退出程序地代码很远。异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。
异常分类
所有的异常都是由Throwable继承而来,到下一层分解为两个分支——Error和Exception。
Error类描述了Java运行时系统的内部错误和资源耗尽错误,应用程序不应该抛出这种类型的对 象。如果出现了这种内部错误,除了通报给用户,并尽力使程序安全地终止以外,再也无能为力了。这种情况很少出现。
在设计Java程序时,需要关注Exception层次结构,这个层次又分为两个分支:一个分支包含RuntimeException(运行时异常),另一个包含其他异常,称为受检异常。划分两个分支地规则是:由程序错误导致地异常属于运行时异常,而程序本身没有问题,但由于像I/O错误这类问题导致地异常属于受检异常。
运行时异常 | 受检异常 |
---|---|
编译可以通过,运行时出现的问题。 | 编译期间不通过。 |
RuntimeException 类 和 它的子类 。 | Exception 类 和 它的部分子类。 |
可以 处理 可以不处理。 | 必须处理 |
常见异常
异常名 | 说明 |
---|---|
ArithmeticException | 算数错误情形,如以零作除数 |
ArrayIndexOutOfBoundException | 数组下标越界 |
NullPointException | 尝试访问null对象成员 |
InputMissmatchException | 欲得到数据类型与实际输入类型不匹配 |
ClassCastException | 对象强制类型转换出错 |
NumberFormatException | 数字格式转换异常 |
public static void main(String[] args) {
// 1.java.lang.ArrayIndexOutOfBoundsException
int [] arr = new int[3];
System.out.println(arr[3]);// 0,1,2
// 2. java.lang.NullPointerException
String str = null;
String[] strs = new String[3];// null,null,null
System.out.println(str.hashCode());
// 3. java.util.InputMismatchException
Scanner input = new Scanner(System.in);
System.out.println("-- 输入一个数字;");
int n = input.nextInt();
System.out.println(n);
// 4.java.lang.ClassCastException
Object obj = new String();
Integer i = (Integer)obj;
// 5. java.lang.NumberFormatException
String s = "123a";
int n = Integer.parseInt(s);
}
异常处理方式
声明已检查异常
方法应该在其首部声明所有可能抛出地异常,
public FileInputStream(String name) throws FileNotFoundException
这个声明表示构造器将根据给定地String参数产生一个FileInputStream对象,但也可能抛出一个FileNotFoundException异常。如发生这种情况时不会初始化一个对象,而是抛出FileNotFoundException对象,之后系统会开始搜索异常处理器,以便知道如何处理FileNotFoundException对象
在自己编写方法时,不必将所有可能抛出地异常都进行声明,遇到下面4中情况时应该抛出异常:
- 调用一个抛出已检查异常的方法
- 程序运行过程中发现错误,并且利用throw语句抛出一个已检查异常
- 程序出现错误,例如,a[-1] = 0会抛出数组越界异常
- Java虚拟机和运行时库出现的内部错误
如果一个方法有可能抛出多个已检查异常,就必须在方法的首部列出所有的异常类,每个异常之间用逗号隔开
但是不需要声明Java的内部错误,即从Error继承的错误,因为任何程序代码都有抛出那些异常的可能。
也不应该声明从RuntimeException继承的那些未检查异常,因为这些运行时错误完全在我们的控制之下,应该将更多的时间花费在修正程序中的错误上,而不是说明这些错误发生的可能性。
总之,一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)
注意:如果在子类中覆盖了父类的一个方法,子类方法中声明的已检查异常不能比父类中声明的异常更通用(也就是说,子类方法中可以抛出更特定的异常,或者根本不抛出任何异常)。如果父类方法没有抛出任何异常,子类也不能抛出。
异常的抛出与捕获
try-catch-(finally)
try{
// 可能会引发异常的代码 // 引发异常 new 异常类类型()
}catch(异常类类型 对象名){
// 捕获 和 处理异常
}
如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,程序将跳过try语句块的其余代码转而执行catch块中的处理器代码,没抛出异常就不执行catch的代码
但如果抛出的异常类型没有在catch中声明,则该方法会立刻退出。所以可能抛出多种异常的情况下要使用多重catch块
try{
// 可能会引发异常的代码 // 引发异常 new 异常类类型()
}catch(异常类类型1 对象名){
// 捕获 和 处理异常
}catch(异常类类型2 对象名){
}……………………
要得到异常对象的更多信息可以使用e.getMessage()得到详细的错误信息(如果有的话)
或者使用e.getClass().getName()得到异常对象的实际类型
再次抛出异常
在catch子句中可以抛出一个异常,这样做的目的是改变异常的类型
try{
code……
}
catch(...Exception e){
throw new ....Exception();
}
如果在一个方法中发生了一个已检查异常,而不允许抛出它,就需要用到包装:捕获这个已检查异常,把它包装成一个运行时异常
try{
}catch(... e){
Throwable se = new ....Exception("...error");
se.initCause(e);
throw se;
}
finally子句
无论try块中是否遇到异常,finally中的语句都会执行,一般用于出现错误需要关闭资源时
自定义异常类
当遇到了任何标准类都没能充分描述清楚的问题时可以创建自己的异常类。通常继承于Exception或者其子类
定义的类应该包含两个构造器,一个默认构造,一个带详细描述信息的构造(超类Throwable的toString方法会打印这些详细信息)
class AgeException extends Exception{
AgeException(){
}
AgeException(String message){
super(message);
}
private String message;
AgeException(String message){
this.message = message;
}
// 重写父类的功能
@Override
public String getMessage() {
return "message:" + message;
}
@Override
public String toString() {
return "string:" + message;
}
@Override
public void printStackTrace() {
System.out.println("stack" + message);
}
}
class Person{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws AgeException{
if(age >= 18 && age <= 65){
this.age = age;
}else{
// 抛异常
throw new AgeException("年龄必须在18-65之间");
}
}
/* public void setAge(int age) throws Exception{
if(age >= 18 && age <= 65){
this.age = age;
}else{
// 抛异常
throw new Exception("年龄必须在18-65之间");
}
}*/
}
public class TestException5 {
public static void main(String[] args) {
Person guojing = new Person();
try {
guojing.setAge(450);
} catch (AgeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(guojing.getAge());
}
}