一、什么是异常
java的基本理念是“结构不佳的代码不能运行”,在编译期间并不能发现所有的错误,余下的问题必须在运行阶段解决。异常处理是java中唯一正式的错误报告机制。异常情形是指阻止当前的方法或者作用域继续执行的问题。在java中通过异常处理机制来处理程序运行期间出现的错误,提升程序的健壮性。
java中与使用其他对象一样,在堆上创建对象来处理异常。针对不同的异常new出不同的异常对象。异常对象的根类是java.lang.Throwable类。在Java中定义了很多异常类(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),这些异常类分为两大类:Error和Exception。Error即无法处理的异常,一旦发生终止程序的运行。Exception,是我们可以处理的异常。Exception包括hecked exception和unchecked exception两类。
unchecked exception(非检查异常),也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。
checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。
在Java中,异常类的结构层次图如下图所示:
在Java中,所有异常类的父类是Throwable类,Error类是error类型异常的父类,Exception类是exception类型异常的父类,RuntimeException类是所有运行时异常的父类,RuntimeException以外的并且继承Exception的类是非运行时异常。常见的异:NullPointerException、IndexOutOfBoundsException、ArrayIndexOutOfBoundsException等等。
异常匹配:抛出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序,找到匹配的处理程序后,则认为异常得到了处理,然后不再继续查找。查找的时候并不要求抛出的异常同处理程序声明的异常完全匹配,派生类对象匹配其基类的处理程序。那么Exception则是所有的异常类的基类,即可捕获所有的异常。
把“受检查异常“转换为“不受检查的异常”,可以在捕获到的受检查异常后,再次抛出一个不受检查的异常。
二、异常的处理处理方式
1、捕获
异常的捕获主要使用try和catch关键字,被try块包围的代码,表示这段代码中可能出现异常,一旦发生异常,异常对象则被cathch捕获,然后在catch块中进行处理。如下:
1 public class Demo5 { 2 public static void main(String[] args) { 3 try { 4 int i=10/0; 5 System.out.println(i); 6 } catch (ArithmeticException e) { 7 e.printStackTrace(System.err); 8 } 9 } 10 } 11 输出的结果: 12 java.lang.ArithmeticException: / by zero 13 at com.sun.lp.demo.Demo5.main(Demo5.java:18)
2、抛出
抛出是异常处理的另外一种方式,顾名思义,一旦发生异常,将异常抛出去,处理的关键字是throw和throws,如下:
public class Demo5 { public void test() throws Exception{ try { int i = 10/0; System.out.println(i); } catch (Exception e) { //捕获到异常,不处理,将其抛出 throw e; } } public static void main(String[] args) { try { Demo5 demo5 = new Demo5(); demo5.test(); } catch (Exception e) { e.printStackTrace(System.err); } } }
结果输出:
java.lang.ArithmeticException: / by zero at com.sun.lp.demo.Demo5.test(Demo5.java:6) at com.sun.lp.demo.Demo5.main(Demo5.java:17)
三、自定义异常
不拘泥于java中已有的异常对象,java提供的异常体系不可能预见所有的异常。所以java提供自定义异常的途径。自定义异常必须从已有异常类的基础上继承和拓展。建立新的异常最简单的方法是让编译器为你产生默认的构造器
1 public class Demo { 2 3 public void test() throws MyException{ 4 System.out.println("Exception is from test()"); 5 throw new MyException(); 6 } 7 8 public static void main(String[] args) { 9 try { 10 Demo demo = new Demo(); 11 demo.test(); 12 } catch (Exception e) { 13 System.out.println("get a Exception"); 14 e.printStackTrace(System.err); 15 } 16 17 } 18 19 } 20 21 class MyException extends Exception{ 22 MyException(){ 23 System.out.println("自定义异常类"); 24 } 25 }
输出结果:
Exception is from test() 自定义异常类 get a Exception com.test.demo.MyException at com.test.demo.Demo.test(Demo.java:5) at com.test.demo.Demo.main(Demo.java:11)
四、使用finally进行清理
对于一些代码,无论try块中的异常是否抛出,它们都能执行,达到这个效果可以使用finally子句。finally子句总能运行。
1 public class FinallyDemo { 2 public static int count=0; 3 public static void main(String[] args) { 4 while(true){ 5 try { 6 if(count++==0) 7 throw new SimpleException(); 8 System.out.println("No Exception"); 9 } catch (SimpleException e) { 10 System.out.println("get a SimpleException"); 11 }finally{ 12 System.out.println("finally 子句执行"); 13 if(count==2) break; 14 } 15 } 16 } 17 } 18 class SimpleException extends Exception{ 19 SimpleException(){ 20 super(); 21 } 22 }
输出结果:
get a SimpleException finally 子句执行 No Exception finally 子句执行
五、几个关键字的使用
1.try关键字用来包围可能会出现异常的逻辑代码,它单独无法使用,必须配合catch或者finally使用。三个块执行的顺序为try—>catch—>finally。Java编译器允许的组合使用形式只有以下三种形式:
try...catch...; try....finally......; try....catch...finally...
2.throws和thow关键字
throws出现在方法的声明中,表示该方法可能会抛出的异常,然后交给上层调用它的方法程序处理,允许throws后面跟着多个异常类型。
throw一般用于程序出现某种逻辑,程序员主动抛出指定的异常对象,throw主要出现在方法体中,抛出的异常对象可以进行向上转型。
3.由于垃圾回收机制的存在,finally主要是把除内存资源之外的资源恢复到它们的初始状态,例如:关闭文件流,网络联机,关闭数据库连接等等。
六、异常使用指南
1)在恰当的级别处理问题(在知道如何处理的情况下才捕获异常)
2)解决问题并重新调用产生异常的方法
3)进行少许的修补,然后绕过异常发生的地方继续执行
4)用别的数据进行计算,以代替方法预计会返回的值
5)把当前运行环境下能做的事情尽量做完,然后把相同的异常抛出到更高层
6)把当前运行环境下能做的事情尽量做完,然后把不相同的异常抛出到更高层
7)终止程序
8)进行简化
9)让类库和程序更安全。