Java之Exception剖析
来源:SegmentFault
时间:2023-01-22 19:00:56 467浏览 收藏
积累知识,胜过积蓄金银!毕竟在##column_title##开发的过程中,会遇到各种各样的问题,往往都是一些细节知识点还没有掌握好而导致的,因此基础知识点的积累是很重要的。下面本文《Java之Exception剖析》,就带大家讲解一下MySQL、Java、PHP、程序员、react.js知识点,若是你对本文感兴趣,或者是想搞懂其中某个知识点,就请你继续往下看吧~
基本概念
- Java使用异常来提供一致性的错误报告模型;且可集中错误处理;且任务代码与异常代码分割开来,易于理解和维护
- 虽然异常处理理论有终止模型、恢复模型两种,但恢复模型很难优雅地做到,∴并不实用,实际中大家都是转向使用终止模型代码
- 一个异常抛出后发生的两件事:① 使用new在堆上创建异常对象;② 异常处理机制开始接管流程(当前的执行流程被终止)
- 标准异常类均有两个ctor:① default ctor; ② 带字符串参数的ctor
- Throwable是异常类型的根类
- catch异常时,try中抛出的是子类异常,但catch的是基类异常也是OK,但若catch子类异常和基类异常的子句同时存在时,应将基类catch子句放在后面避免“屏蔽”现象发生
抛出异常 + 捕获异常
- 抛出异常(throw):
if( t==null ) throw new NullPointerException(); // 异常对象用new创建于堆上 12
- 捕获异常(try+catch):
try { ... } catch( Type1 id1 ) { // 处理Type1类型的异常代码 } catch( Type2 id2 ) { // 处理Type2类型的异常代码 } 1234567
- 虽然上面的id1和id2在处理异常代码中可能用不到,但不能少,必须定义
- 异常发生时,异常机制搜寻参数与异常类型相匹配的第一个catch子句并进入
创建自定义异常
创建不带参数ctor的自定义异常类:
// 自定义异常类(default ctor) class SimpleException extends Exception {} ------------------------------------------------------------ // 客户端代码 public class UseException { public void fun throws SimpleException { System.out.println( "Throw SimpleExcetion from fun" ); throw new SimpleException(); } public static void main( String[] args ) { UseException user = new UseException(); try { user.fun(); } catch( SimpleException e ) { System.out.println("Caught it !"); } } } ------------------------------------------------------------ // 输出 Throw SimpleExcetion from fun Caught it ! 123456789101112131415161718192021222324
创建带参数ctor的自定义异常类
// 自定义异常类(有参ctor) class MyException extends Exception { public MyException() { } public MyException( String msg ) { super(msg); } } ------------------------------------------------------------ // 客户端代码 public class UseException { pubilc static void f() throws MyException { System.out.println( "Throwing MyException from f()" ) throw new MyException(); } public static void g() throws MyException { System.out.println( "Throwing MyException from g()" ) throw new MyException("Originated in g()"); } publib static void main( String[] args ) { try { f(); } catch( MyException e ) { e.printStackTrace( System.out ); } try { g(); } catch( MyException e ) { e.printStackTrace( System.out ); } } } ------------------------------------------------------------ // 输出 Throwing MyException from f() MyException at ... at ... Throwing MyException from g() MyException: Originated in g() // 此即创建异常类型时传入的String参数 at ... at ... 123456789101112131415161718192021222324252627282930313233343536373839404142434445
捕获所有异常
try { ... } catch( Exception e ) { // 填写异常的基类,该catch子句一般置于末尾 ... } 12345
Exception类型所持有的方法:
- String getMessage()
- String getLocalizedMessage()
- String toString()
- void printStackTrace()
- void printStackTrace( PrintStream )
- void printStackTrace( javo.io.PrintWriter )
注意:从下往上每个方法都比前一个提供了更多的异常信息!
栈轨迹
printStackTrace()方法所提供的栈轨迹信息可以通过getStackTrace()方法来Get,举例:
try { throw new Exception(); } catch( Exception e ) { for( StackTraceElement ste : e.getStackTrace() ) System.out.println( ste.getMethodName() ); } 123456
这里使用getMethodName()方法来给出异常栈轨迹所经过的方法名!
重抛异常
try { ... } catch( Exception e ) { throw e; // 重新抛出一个异常! } 12345
若只是简单地将异常重新抛出,则而后用printStackTrace()显示的将是原异常抛出点的调用栈信息,而非重新抛出点的信息,欲更正该信息,可以使用fillInStackTrace()方法:
try { ... } catch( Exception e ) { throw (Exception)e.fillInStackTrace(); // 该行就成了异常的新发生地! } 12345
异常链
异常链:在捕获一个异常后抛出另一个异常,并希望将原始的异常信息保存下来!
解决办法:
\1. 在异常的ctor中加入cause参数
\2. 使用initCause()方法
注意:Throwable子类中,仅三种基本的异常类提供了待cause参数的ctor(Error、Exception、RuntimeException),其余情况只能靠initCause()方法,举例:
class DynamicFieldsException extends Exception { } public Object setField( String id, Object value ) throws DynamicFieldsException { if( value == null ) { DynamicFieldsException dfe = new DynamicFieldsException(); dfe.initCause( new NullPointerException() ); throw dfe; } Object result = null; try { result = getField(id); } catch( NoSuchFieldException e ) { throw new RuntimeException( e ); } } 123456789101112131415161718
Java标准异常
- 看这个图需要明确:程序员一般关心Exception基类型的异常
- 由图中可知,Error、RuntimeException都叫做“Unchecked Exception”,即不检查异常,程序员也无需写异常处理的代码,这种自动捕获
- 若诸如RuntimeException这种Unchecked异常没有被捕获而直达main(),则程序在退出前将自动调用异常的printStackTrace()方法
使用finally进行清理
try { ... } catch(...) { ... } finally { // finally子句总是会被执行!!! ... } 1234567
使用时机:
- 当需要把内存之外的资源(如:文件句柄、网络连接、某个外部世界的开关)恢复到初始状态时!
try { ... } catch(...) { ... } finally { // finally子句总是会被执行!!! sw.off(); // 最后总是需要关掉某个开关! } 1234567
- 在return中使用finally
public static void func( int i ) { try { if( i==1 ) return; if( i==2 ) return; } finally { print( "Performing cleanup!" ); // 即使上面有很多return,但该句肯定被执行 } } 123456789101112
finally存在的缺憾:两种情况下的finally使用会导致异常丢失!
- 前一个异常还未处理就抛出下一个异常
// 异常类 class VeryImportantException extends Exception { poublic String toString() { return "A verfy important exception!"; } } class HoHumException extends Exception { public String toString() { return "A trivial exception!"; } } ------------------------------------------------------------------ // 使用异常的客户端 public class LostMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main( String[] args ) { try { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); // 最后只会该异常生效,lm.f()抛出的异常丢了! } } catch( Exception e ) { System.out.println(e); } } } ----------------------------------------------------------------- // 输出 A trivial exception! 123456789101112131415161718192021222324252627282930313233343536373839
- finally子句中的return
public static void main( String[] args ) { try { throw new RuntimeException(); } finally { return; // 这将会掩盖所有的异常抛出 } } 1234567
继承基类、实现接口时的异常限制
// 异常类 class A extends Exception { } class A1 extends A { } class A2 extends A { } class A1_1 extends A1 { } class B extends Exception { } class B1 extends B { } ------------------------------------------------- // 用了异常类的基类 abstract class Base { public Base() throws A { } public void event() throws A { } // (1) public abstract void atBat throws A1, A2; public void walk() { } } ------------------------------------------------- // 用了异常类的接口 interface Interf { public void event() throws B1; public void rainHard() throws B1; } ------------------------------------------------- // 继承基类并实现接口的客户端类 public class Ext extends Base implements Interf { public Ext() throws B1, A { } // (2) public Ext( String s ) throws A1, A {} // (2) public void walk() throws A1_1 { } // (3) 编译错误! public void rainHard() throws B1 {} // (4) public void event() { } // (5) public void atBat() throws A1_1 { } // (6) public static void main( String[] args ) { try { Ext ext = new Ext(); ext.atBat(); } catch( A1_1 e ) { ... } catch( B1 e ) { ... } catch( A e ) { ... } try { Base base = new Ext(); ext.atBat(); } catch( A2 e ) { // 这里的catch必须按照Base中函数的异常抛出来写 ... } catch( A1 e ) { ... } catch( B1 e ) { ... } catch( A ) { ... } } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
上面的例子可以总结如下:【注意对应数字标号】
- (1) 基类的构造器或者方法声明了抛出异常,但实际上没有,这里相当于为继承类写了一个异常抛出规范,子类实现时安装这个规范来抛异常
- (2) 从这两个ctor看出:异常限制对ctor不生效,子类ctor可以抛出任何异常而不管基类ctor所抛出的异常
- (3) 基类函数没抛异常,派生类重写时不能瞎抛!
- (4) 完全遵守基类的抛出,正常情况
- (5) 基类函数抛了异常,派生类重写时不抛也是OK的
- (6) 派生类重写基类函数时抛的异常可以是基类函数抛出异常的子类型
构造器中异常如何书写
对于在构造阶段可能会抛出异常并要求清理的类,安全的方式是使用嵌套的try子句:即在创建需要清理的对象之后,立即进入一个try-finally块,举例:
特别需要注意的是下面的例子里在ctor中对文件句柄的close应放置的合理位置!
// 需要清理的对象类 class InputFile { private BufferedReader in; InputFile( String fname ) throws Exception { // 构造函数! try { in = new BufferedReader( new FileReader(fname) ); // 这里放置可能抛出异常的其他代码 } catch( FileNotFoundException e ) { // 若上面的FileReader异常,将会抛FileNotFoundException,走到这里,该分支无需in.close()的 System.out.println( "Could not open " + fname ); throw e; } catch( Exception e ) { // 走到这里其实说明in对象已经构建成功,这里是必须in.close()的 try { in.close(); // 注意此处关闭动作单独用try进行保障 } catch( IOException e2 ) { System.out.println("in.close() unsuccessful"); } throw e; } finally { // 注意in.close() 不要在此处关闭,因为try中假如BufferedReader构造失败,此时in对象未生成成功,是无需close()一说的! } } String getLine() { String s; try { s = in.readLine(); } catch( IOException e ) { System.out.println( "readLine() unsuccessful!" ); s = "failed"; } return s; } void cleanup() { // 提供手动的关闭文件句柄的操作函数 try { in.close(); } catch( IOException e ) { System.out.println( "in.close() failed !" ); } } } ---------------------------------------------------- // 客户端代码 public class Cleanup { public static void main( String[] args ) { try { InputFile in = new InputFile( "Cleanup.java" ); try { // 上面InputFile构造完成以后立即进入该try-finally子句! String s = ""; int i = 1; while( (s = in.getLine()) != null ) System.out.println(""+ i++ + ": " + s); } catch( Exception e ) { e.printStackTrace( System.out ); } finally { // 该finally一定确保in能正常cleanup()! in.cleanup(); } } catch( Exception e ) { System.out.println( "InputFile ctor failed!" ); } } // end main() } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 while( (s = in.getLine()) != null ) System.out.println(""+ i++ + ": " + s); } catch( Exception e ) { e.printStackTrace( System.out ); } finally { // 该finally一定确保in能正常cleanup()! in.cleanup(); } } catch( Exception e ) { System.out.println( "InputFile ctor failed!" ); } } // end main() } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
到这里,我们也就讲完了《Java之Exception剖析》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于mysql的知识点!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
475 收藏
-
266 收藏
-
273 收藏
-
283 收藏
-
210 收藏
-
371 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 忧郁的手链
- 好细啊,码住,感谢作者的这篇技术贴,我会继续支持!
- 2023-05-19 07:10:38
-
- 老实的汉堡
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢up主分享博文!
- 2023-05-07 23:29:18
-
- 个性的电脑
- 这篇博文太及时了,太详细了,感谢大佬分享,收藏了,关注up主了!希望up主能多写数据库相关的文章。
- 2023-03-29 21:18:50
-
- 秀丽的猫咪
- 这篇博文太及时了,up主加油!
- 2023-03-09 01:05:10
-
- 炙热的河马
- 太全面了,码住,感谢博主的这篇文章内容,我会继续支持!
- 2023-03-07 16:01:23
-
- 殷勤的小蘑菇
- 受益颇多,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢up主分享文章!
- 2023-02-21 14:03:28
-
- 顺利的方盒
- 这篇技术文章真及时,很详细,很棒,码起来,关注大佬了!希望大佬能多写数据库相关的文章。
- 2023-02-17 20:58:53
-
- 聪慧的战斗机
- 赞 👍👍,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢up主分享技术文章!
- 2023-02-09 20:42:18
-
- 追寻的红牛
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢博主分享技术贴!
- 2023-02-09 10:28:47
-
- 端庄的红牛
- 这篇文章内容太及时了,太详细了,很好,已收藏,关注作者大大了!希望作者大大能多写数据库相关的文章。
- 2023-02-05 16:14:52
-
- 害怕的楼房
- 细节满满,mark,感谢作者的这篇博文,我会继续支持!
- 2023-02-05 09:15:47
-
- 靓丽的雪碧
- 这篇博文真及时,楼主加油!
- 2023-01-29 18:22:08
-
- 机灵的小土豆
- 这篇博文真是及时雨啊,作者加油!
- 2023-01-28 16:24:32
-
- 知性的酸奶
- 这篇博文出现的刚刚好,太全面了,写的不错,码起来,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-01-27 07:05:59
-
- 淡淡的路人
- 太全面了,码住,感谢作者的这篇技术文章,我会继续支持!
- 2023-01-23 18:13:59