Java 常用类解析:java异常机制,异常栈,异常处理方式,异常链,异常丢失详解
1、java标准异常概述
Throwable表示任何可以作为异常被抛出的类,有两个子类Error和Exception。从这两个类的源代码中可以看出,这两个类并没有添加新的方法,Throwable提供了所以方法的实现。Error表示编译时和系统错误。Exception是可以被抛出的异常类。RuntimeException继承自Exception(如NullPointerException),表示运行时异常,JVM会自动抛出.
2、自定义异常类
自定义异常类方法:通过继承Throwable或Exception。异常类的所有实现都是基类Throwable实现的,所以构造自定义异常类完全可以参考Exception和Error类。我们只要添加上自定义异常类的构造方法就可以了
packagedemo.others; /** *自定义异常类方法 *1、通过继承Throwable *2、通过继承Exception * *@authorTouch */ publicclassMyExceptionDemoextendsException{ privatestaticfinallongserialVersionUID=1L; publicMyExceptionDemo(){ super(); } publicMyExceptionDemo(Stringmessage){ super(message); } publicMyExceptionDemo(Stringmessage,Throwablecause){ super(message,cause); } publicMyExceptionDemo(Throwablecause){ super(cause); } }
3、异常栈及异常处理方式
可以通过try、catch来捕获异常。捕获到的异常。下面的示例演示了几种常用异常处理方式
packagedemo.others; importmine.util.exception.MyException; publicclassExceptionDemo1{ publicvoidf()throwsMyException{ thrownewMyException("自定义异常"); } publicvoidg()throwsMyException{ f(); } publicvoidh()throwsMyException{ try{ g(); }catch(MyExceptione){ //1、通过获取栈轨迹中的元素数组来显示异常抛出的轨迹 for(StackTraceElementste:e.getStackTrace()) System.out.println(ste.getMethodName()); //2、直接将异常栈信息输出至标准错误流或标准输出流 e.printStackTrace();//输出到标准错误流 e.printStackTrace(System.err); e.printStackTrace(System.out); //3、将异常信息输出到文件中 //e.printStackTrace(newPrintStream("file/exception.txt")); //4、重新抛出异常,如果直接抛出那么栈路径是完整的,如果用fillInStackTrace() //那么将会从这个方法(当前是h()方法)作为异常发生的原点。 //throwe; throw(MyException)e.fillInStackTrace(); } } publicstaticvoidmain(String[]args){ try{ newExceptionDemo1().h(); }catch(MyExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } }
运行结果:
f g h main mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo1.f(ExceptionDemo1.Java:7) atdemo.others.ExceptionDemo1.g(ExceptionDemo1.Java:11) atdemo.others.ExceptionDemo1.h(ExceptionDemo1.java:16) atdemo.others.ExceptionDemo1.main(ExceptionDemo1.java:35) mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo1.f(ExceptionDemo1.java:7) atdemo.others.ExceptionDemo1.g(ExceptionDemo1.java:11) atdemo.others.ExceptionDemo1.h(ExceptionDemo1.java:16) atdemo.others.ExceptionDemo1.main(ExceptionDemo1.java:35) mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo1.f(ExceptionDemo1.java:7) atdemo.others.ExceptionDemo1.g(ExceptionDemo1.java:11) atdemo.others.ExceptionDemo1.h(ExceptionDemo1.java:16) atdemo.others.ExceptionDemo1.main(ExceptionDemo1.java:35) mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo1.h(ExceptionDemo1.java:30) atdemo.others.ExceptionDemo1.main(ExceptionDemo1.java:35)
分析上面的程序,首先main函数被调用,然后是调用h函数,再g函数、f函数,f函数抛出异常,并在h函数捕获,这时将依次从栈顶到栈底输出异常栈路径。
4、异常链
有时候我们会捕获一个异常后在抛出另一个异常,如下代码所示:
packagedemo.others; importjava.io.IOException; importmine.util.exception.MyException; publicclassExceptionDemo2{ publicvoidf()throwsMyException{ thrownewMyException("自定义异常"); } publicvoidg()throwsException{ try{ f(); }catch(MyExceptione){ e.printStackTrace(); thrownewException("重新抛出的异常1"); } } publicvoidh()throwsIOException{ try{ g(); }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); thrownewIOException("重新抛出异常2"); } } publicstaticvoidmain(String[]args){ try{ newExceptionDemo2().h(); }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } }
运行结果:
mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo2.f(ExceptionDemo2.java:9) atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:14) atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:23) atdemo.others.ExceptionDemo2.main(ExceptionDemo2.java:32) java.lang.Exception:重新抛出的异常1 atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:17) atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:23) atdemo.others.ExceptionDemo2.main(ExceptionDemo2.java:32) java.io.IOException:重新抛出异常2 atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:27) atdemo.others.ExceptionDemo2.main(ExceptionDemo2.java:32)
从结果中我们可以看出,异常栈变小了。也就是说丢失了最原始的异常信息。怎样保存最原始的异常信息呢?Throwable类中有个Throwable cause属性,表示原始异常。通过接收cause参数的构造器可以把原始异常传递给新异常,或者通过initCause()方法。如下示例:
packagedemo.others; importjava.io.IOException; importmine.util.exception.MyException; publicclassExceptionDemo2{ publicvoidf()throwsMyException{ thrownewMyException("自定义异常"); } publicvoidg()throwsException{ try{ f(); }catch(MyExceptione){ e.printStackTrace(); thrownewException("重新抛出的异常1",e); } } publicvoidh()throwsIOException{ try{ g(); }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); IOExceptionio=newIOException("重新抛出异常2"); io.initCause(e); throwio; } } publicstaticvoidmain(String[]args){ try{ newExceptionDemo2().h(); }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } }
结果:
mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo2.f(ExceptionDemo2.java:9) atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:14) atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:23) atdemo.others.ExceptionDemo2.main(ExceptionDemo2.java:34) java.lang.Exception:重新抛出的异常1 atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:17) atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:23) atdemo.others.ExceptionDemo2.main(ExceptionDemo2.java:34) Causedby:mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo2.f(ExceptionDemo2.java:9) atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:14) ...2more java.io.IOException:重新抛出异常2 atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:27) atdemo.others.ExceptionDemo2.main(ExceptionDemo2.java:34) Causedby:java.lang.Exception:重新抛出的异常1 atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:17) atdemo.others.ExceptionDemo2.h(ExceptionDemo2.java:23) ...1more Causedby:mine.util.exception.MyException:自定义异常 atdemo.others.ExceptionDemo2.f(ExceptionDemo2.java:9) atdemo.others.ExceptionDemo2.g(ExceptionDemo2.java:14) ...2more
从结果中看出当获取到“重新抛出异常2的时候,同时可以输出原始异常“重新抛出的异常1“和原始异常”自定义异常,这就是异常链。
5、finally的使用
finally子句总是执行的,通常用来做一些清理工作,如关闭文件,关闭连接等
下面举几个finally的例子:
//读取指定路径文本文件 publicstaticStringread(StringfilePath){ StringBuilderstr=newStringBuilder(); BufferedReaderin=null; try{ in=newBufferedReader(newFileReader(filePath)); Strings; try{ while((s=in.readLine())!=null) str.append(s+'\n'); }finally{ in.close(); } }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } returnstr.toString(); }
分析:如果调用in=newBufferedReader(newFileReader(filePath));时发生异常,这时是一个文件路径不存在的异常,也就是说并没有打开文件,这时将会直接跳到catch块,而不会执行try...finally块(并不是finally子句)里面的语句in.close();此时不需要关闭文件。
再看一个例子,会导致异常的丢失
packagedemo.others; importmine.util.exception.MyException; publicclassExceptionDemo3{ publicvoidf()throwsMyException{ thrownewMyException("异常1"); } publicvoidg()throwsMyException{ thrownewMyException("异常2"); } publicstaticvoidmain(String[]args){ try{ ExceptionDemo3ex=newExceptionDemo3(); try{ ex.f(); }finally{ ex.g();//此时捕获g方法抛出的异常,f方法抛出的异常丢失了 } }catch(MyExceptione){ System.out.println(e); } } }
结果:mine.util.exception.MyException:异常2
此时异常1就丢失了
或者这样写:
packagedemo.others; importmine.util.exception.MyException; publicclassExceptionDemo3{ publicvoidg()throwsMyException{ thrownewMyException("异常2"); } publicstaticvoidmain(String[]args){ ExceptionDemo3ex=newExceptionDemo3(); try{ ex.g(); }finally{ //直接return会丢失所以抛出的异常 return; } } }
6、异常的限制
(1)当覆盖方法时,只能抛出在基类方法的异常说明里列出的那些异常,有些基类的方法声明抛出异常其实并没有抛出异常,这是因为可能在其子类的覆盖方法中会抛出异常
(2)构造器可以抛出任何异常而不必理会基类构造器所抛出的异常,派生类构造器异常说明必须包含基类构造器异常说明,因为构造派生类对象时会调用基类构造器。此外,派生类构造器不能捕获基类构造器抛出的异常。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!