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子句一般置于末尾
...
}
12345Exception类型所持有的方法:
- 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 );
}
}
123456789101112131415161718Java标准异常

- 看这个图需要明确:程序员一般关心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,但该句肯定被执行
}
}
123456789101112finally存在的缺憾:两种情况下的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 收藏
-
117 收藏
-
411 收藏
-
420 收藏
-
264 收藏
-
266 收藏
-
392 收藏
-
333 收藏
-
234 收藏
-
448 收藏
-
416 收藏
-
225 收藏
-
145 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习