从事Java
开发的小伙伴对于“异常”应该不陌生,由于天天都会遇到很多异常,或捕获,或抛出。那究竟什么是异常?异常即非正常的,不一样于日常、通常化的状况。java
在平时生活中,医生会说你身体的某个部位有异常,该异常会有什么什么的影响,是由某某缘由引发的;程序员
再好比:我天天都准时打卡,按时上下班,那么我本月的考勤是正常的,反之,但凡是有迟到、旷工、早退的状况之一的,我本月的考勤就会有异常。数据库
而在程序中,代码在运行中若是出现运行错误,程序会终止运行,这时由于错误致使程序运行终止的状况就是程序出现了异常。编程
异常并非指语法错误,由于若是语法错了,编译就通不过,不会产生JVM可以识别的字节码文件,是无法运行起来的,因此只有运行中的程序才会有异常一说。数组
异常处理是衡量一门语言是否成熟的标准之一,C
系列的语言诸如:Java
、C++
、C
等都支持异常处理,有本身的一套异常处理机制。异常处理机制可让程序有更好的容错性,使代码更加健壮。网络
因此,引入异常处理机制能够解决的问题有:ide
经过一下这个案例来讲明引入异常以前的处理方式:性能
Car.java:spa
Officer.java:3d
WorkDemo.java:
上述案例中,只是简单粗暴地把汽车的状态分为true
和false
两种,细化不够,不能体现出汽车的详细状态;业务逻辑也很局限,若是要拓展,就要花费更大的成本。
针对于上述的问题,Java
基于面向对象的思想提出了解决方案:
Java API文档中的详细介绍以下:
Error
:表示错误,通常指JVM
相关的不可修复的错误,如:系统崩溃、内存溢出、JVM
内部错误等,由JVM
抛出,通常状况下不须要处理,几乎其全部的子类都是以“Error
”做为类名后缀;好比:StackOverflowError
,当应用程序递归太深而发生内存溢出时,就会抛出该错误。
Exception
:表示异常,指程序中出现不正常的状况,异常通常都是须要程序员来处理的(能够捕获或者抛出);几乎其全部的子类都是以“Exception
”做为类名的后缀;
常见的Exception
有:
1.NullPointerException
:空指针异常,通常当对象为null
的时候,对该对象作操做时会出现该异常;
2.ArrayIndexOutOfBoundsException
: 数组的索引越界,操做数组时使用的索引超出了数组的数据范围会出现;
3.NumberFormatException
:数字格式化异常,把非数字的数据类型转换为数字类型时使用了非法的转换对象;
这里只列出了三种,但Java
中的异常类型远不止这几种,想要了解更多能够去查阅JDK
文档。
Throwable:在Java
体系中,Throwable
类是全部错误和异常的父类;当出现了没见过的异常时,能够将异常类的类名拿到Java API
文档中去查找,经过文章介绍便可得到异常的详细信息,以及其在Java
中的继承、实现体系;
如下是一段运行会出异常的Java 代码:
运行上述代码案例,运行结果以下:
若是出现异常,会马上中断运行中的程序,因此必须处理异常,而处理方式有两种:
throws
:当前方法不处理,而是声明抛出,由该方法的调用者来处理;try-catch
:在当前方法中使用try-catch
的语句块来处理异常;出现异常以后,程序会中断执行,因此异常必须处理;处理的方式有两种:捕获和抛出,两种方式二选一便可。先来运行一个案例来证实:
运行结果以下:
经过查看运行结果,是指望的运行结果,代码运行成功;那么接下来对上述案例稍做修改,再来看其运行结果如何:
运行结果以下:
经过查看运行结果,运行结果并非想要的,代码中出现了异常,代码被中断运行。对比两次的运行结果,能够得出结论:出现异常以后,程序会中断执行,异常必须处理。
try-catch 异常捕获:使用try-catch
捕获单个异常,语法以下:
注意:try和catch都不能单独使用,必须连用。因此能够对上述出现异常的代码使用try-catch来处理:
运行结果以下:
经过查看运行结果,不难发现,使用try-catch
以后,程序遇到异常时再也不中断执行,而是跳过异常代码及其以后的在try-catch
中的剩余代码语句,来到catch
代码块中执行定义的异常处理代码;
在上述案例中是打印出了异常信息、异常对象信息、异常栈追踪,其中的3
个方法都是Throwable
类的方法:
String getMessage()
:获取异常的详细描述信息;String toString()
:获取异常的类型、异常描述信息;void printStackTrace()
:打印异常的跟踪栈信息并输出到控制台,但不能在System.out.println()
中使用该方法;其中包含了异常的类型、异常的缘由、异常出现的位置;在开发和调试阶段,该方法都颇有用,方便调试和修改;而在Java
底层,当代码出现异常时,JVM
会先建立对应的异常类型对象,而后根据异常类型在catch
中进行匹配;
若匹配成功,则会把建立好的异常对象赋值给catch
中声明的异常对象;若匹配失败,则会向上抛出异常。
使用try-catch
捕获多个异常,语法以下:
这里方式其实就是在单个异常捕获的基础上添加了多个异常的匹配,使得异常处理更加精细化。
try-catch
时须要注意:catch
语句,只能捕获一种类型的异常,若是须要捕获多种异常类型,就得使用多个catch
语句;try-catch
中的代码在只会出现一种类型的异常,只能一个catch
捕获,不可能同时匹配多个catch
;catch
语句的代码中出现异常时,会从上到下依次匹配catch
语句,因此多个catch
语句应该按照从子类到父类的顺序依次定义;catch
以后,便不会匹配剩余的catch
,而是会跳出try-catch
,执行以后的代码;运行结果以下:
异常被成功捕获,再无法瞎蹦哒了。
抛出异常有两种方式:
throw
:用于方法内部,用于给调用者返回一个异常对象,和return
同样会结束当前方法;throws
:运用于方法声明之上,定义于方法参数以后,表示当前方法不处理异常,而是提醒该方法的调用者来处理抛出的异常(一个或者多个异常);如:private static int divide(int num1, int num2) throws Exception {}
throw
语句:运用于方法内部,抛出一个具体的异常对象,停止方法的执行,其语法格式以下:throw new 异常类("异常信息");
通常的,当一个方法出现异常的状况,不知道该方法应该返回什么时,此时就能够返回一个错误,在catch
语句块中使用throw
继续向上抛出异常。return
是返回一个值,throw
是返回一个错误,返回给该方法的调用者。好比:String
类的charAt
方法就是一个很好的例子:
throws
语句:若是每个方法都放弃处理异常都直接经过throws
声明抛出,最后异常会抛到main
方法,若是此时main
方法还不处理,会继续抛出给JVM
,JVM
底层的处理机制就是打印异常的跟踪栈信息;runtime
异常,默认就是这种处理方式。
一同: 方法的签名必须相同。
两小:
1. 子类方法返回类型和父类方法返回类型相同,或是其子类;
2. 子类方法不能声明抛出新的异常;
一大: 子类方法的访问权限必须大于等于父类方法的访问权限。
下图是Java
中的异常分类体系,Java
中全部的异常都从Throwable
继承而来,主要分两大类:Error
(错误)和异常(Exception
)。
异常(Exception
)根据其在编译时期仍是运行时期去检查异常可分为:checked异常
和runtime异常
runtime异常
:又称运行时期异常
,此类型的异常在运行时期检查;在编译时期,运行异常并不会检测,就不会出现,只有在运行到相关代码时才会出现;RuntimeException
自身及其子类异常都属于runtime异常
;
checked异常
:又称编译时期异常
,此类型的异常在编译时期就会检查,并且是必须处理的,若是没有处理,就会致使编译失败;除了runtime异常
以外的其余异常(包括Exception自身)都属于checked异常
;
Java
中有着不一样的定义好的异常类,分别表示着某一种具体的异常状况,在开发中老是有些异常状况是Java SE
库中没有定义好的,此时就能够根据本身业务的异常状况来定义异常类;把这样的异常类称为自定义异常类。
受检查的异常:即checked异常
, 自定义一个受检查的异常类须要继承于java.lang.Exception
;
运行时异常:即runtime异常
, 自定义一个运行时期检查的异常类,须要继承于java.lang.RuntimeException
;通常在开发中,自定义的异常都是运行时异常。
如今就可使用自定义异常来解决开车上班的案例中的异常问题:
异常转译:位于最外层的业务系统不须要关心底层的异常细节,经过捕获原始的异常,将其转换为一个新的不一样类型的异常,而后再向上抛出;这个过程称为异常转译。
在上述例子中:个人车坏了,在catch
中从新抛出一个新的异常(OfficerException
)给个人调用者(老板),不能把车的异常信息抛给老板看,由于老板不关心这些细节,关心的是我是否迟到。
异常链: 把原始异常包装为新的异常类,造成多个异常的有序排列;异常链因为更加清楚、准确的定位异常出现的位置;在下述案例中,异常一层层抛出,直至异常被处理,在这个过程当中,异常链就产生了:
1.加强的throw
: 对比Java 6
和 Java 7
中对于抛出异常的改进来体现。
2.多异常捕获: 重写捕获多个异常案例来体现。
3.自动资源关闭: 资源类必须直接或者间接实现java.lang.AutoCloseable
接口
finally
语句块表示不管如何(也包括发生异常时)都会最终执行的代码块,好比:当在try
语句块中打开了一些物理资源(磁盘文件/网络链接/数据库链接等)时,在使用完以后,都得最终关闭打开的资源。
1.try...finally
: 此时没有catch
来捕获异常,由于此时根据应用场景会抛出异常,程序员本身不处理;
2.try...catch....finally
: 程序员本身须要处理异常,最终得手动关闭资源。
须要注意的是:finally
不能单独使用。
finally
不执行的状况:当只有在try
或者catch
中调用退出JVM
的相关方法,此时finally
才不会执行,不然finally
修饰的代码块永远会执行。好比:System.exit(0);//退出JVM
。
finally
和 return
若是finally
和return
语句同时存在,永远返回finally
中的结果,可是应该避免这种状况的出现。详情看以下的案例:
若是finally
和return
语句同时存在,永远返回finally
中的结果
还有另外一种状况也颇有趣,一块儿来看看:
为何会出现这种状况呢?首先finally确定是会被执行的,因此a++
以后a
的值变成了14
,可是finally
中没有返回值,值为14
的变量a
并无被返回;而后接着执行return a
;这里的a
的值在方法执行之初就已经肯定了,故返回的值是13
。
1. 异常只能用于非正常状况,try-catch
的存在也会影响性能,尽可能缩小try-catch
的代码范围;
2. 须要为异常提供说明文档,能够参考Java doc
,若是自定义了异常或某一个方法抛出了异常,应该在文档注释中详细说明;
3. 尽量避免异常的出现,如NullPointerException
等;
4. 异常的粒度很重要,应该为一个基本操做定义一个 try-catch
块,切忌将几百行代码放到一个 try-catch
块中;
最后,不要在循环中进行异常处理,应该在循环外对异常进行捕获处理(在循环以外使用try-catch
);自定义异常尽可能使用RuntimeException
类型的,而且要尽可能避开已存在的异常;
1. 五个关键字:try、catch、finally、throw、throws
;
2. 异常体系的两个继承结构:
Throwable 类有两个子类:Error和Exception。 Exception 类有一个特殊的子类:RuntimeException。 RuntimeException 类及其子类称之为runtime异常。 Exception 类和子类中除了RuntimeException体系的其余类称之为checked异常。
3. 自定义异常类
4. Error
和Exception
的区别和关系
5. checked异常
和runtime异常
的区别
6. finally
关键字及其相关知识
7. finally和return的执行顺序
8. throw
和throws
的区别
完结,老夫虽不正经,但老夫一身的才华!关注我,获取更多编程科技知识。