java异常详细解读
什么是异常?异常是程序中导致程序中断的一段指令流。简单来说,就是你程序运行到某个地方,出错了,会返回一段记录错误的信息。我们可以根据这段错误信息,更好地定位错误的原因,进而对程序进行修改,打补丁。如果程序出现异常,抛出的异常在当前这一级别没有得到处理,异常会不断向上一级抛出,直到异常被处理,或者到达你运行的main方法处,抛给JVM虚拟机,从而引发程序崩溃。
下图中程序可以比较清楚的表达这一过程,不同的是,在这里出现异常的地方在main方法下,程序会直接把异常抛给上一级JVM。而这导致的结果就是,程序直接崩溃,奔溃之后的程序无法运行。在多线程工作环境中,这往往会影响到其他线程的工作。
异常的体系结构
java有着丰富的多态特征,这一点同样体现在异常这一体系上,为了更好理解,先上图:
异常类和其他java类一样,有着共同的父类Object,而真正让这些类“异常”于其他类的,是父类Throwable,顾名思义,异常类就是用来抛出的。而其中,Error发生代表无法在纯程序层面上进行修复,比如内存溢出。真正考验程序员功底的在Excpetion类,这一部分在理论上可以被程序员修复。
写过java程序的伙伴们会发现,在写一些代码时,编译器会提醒你需要对代码中的某一部分进行异常抛出,并做了强制性要求。而在其他一些可能出现异常的程序,却没有这种强制性的要求。原因在于,前者是受检异常,而后者是非受检异常。前者诸如做一些IO接口连接时,后者诸如算术运算中出现除数为0。详细看图,自行百度浏览官方文件,这里不多篇幅。
抛出异常
记住这句话,原则上,我们要尽可能地为程序可能出现的异常做修补,而并非简单抛出。这也是java设置异常体系的另一个重要原因。语法简要带过
try{
// 有可能发生异常的代码段
}
catch(异常类型1 对象名1){
// 异常的处理操作
}
catch(异常类型2 对象名2){
// 异常的处理操作
}
...
finally{
// 异常的统一出口
}
图已经说的很清楚了,对try中可能出现的异常,在catch处进行异常的处理,包括打补丁等操作,finally放的程序是必然运行的,除了强制中断等造成JVM在内存中消失的,更详细可以参考另外这篇文章。
直接上代码例子
public class Demo3 {
public static void main(String[] args){
haha();
System.out.println("程序执行完毕 , 正常结束");
}
private static void haha() {
try {
Scanner input = new Scanner(System.in);
System.out.println("请输入一个数字");
int x = input.nextInt();
System.out.println("请再输入一个数字");
int y = input.nextInt();
System.out.println(x / y);
System.out.println("处理完毕");
}catch(InputMismatchException|ArithmeticException e){
System.out.println("输入有误");
}
}
}
当我们输入y=0时,程序在做x/y运算时会抛出ArithmeticException类型的异常。当输入x = "abc"的字符串时,程序在input.nextInt()处抛出InputMismatchException类型的异常。且会在catch出进行异常处理,因此,当运行main时,haha方法会抛出异常,但异常在haha方法内就被处理,不会抛给main方法,JVM不会崩溃,haha方法之后的程序得以正常运行。但这里只是对异常的信息做了简要描述,我们更希望,当程序发生异常时,catch里的代码块能起到些许补救作用。
看另一个例子
public class Test1 {
public static void main(String[] args) {
int num = menu();
System.out.println("你输入的是:"+num);
}
public static int menu(){
System.out.println("请根据提示,选择功能序号:");
System.out.println("1. 增加XX");
System.out.println("2. 删除XX");
System.out.println("3. 修改XX");
System.out.println("0. 退出");
Scanner input = new Scanner(System.in);
int num = -1;
try{
num = input.nextInt();
if(num<0 || num>3){
//程序有问题 , 输入有误
System.out.println("功能序号必须是: 0/1/2/3");
return menu();
}
return num;
}catch(InputMismatchException e){
//System.out.println("哈哈哈, 程序出错了.");
//补救
System.out.println("必须输入数字哦");
return menu();
}
}
}
这个例子中catch就很好地起到这一点,输入值num要求是0/1/2/3中的一个数,当用户输入错误时,我们并不希望程序终止,而更希望让用户重新输入。因此,在catch代码块里返回了一个menu方法,重新进入该方法,让用户重新输入合理的num值。