本系列文章旨在记录和总结自己在Java Web开发之路上的知识点、经验、问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人。

目录

  1. 介绍
  2. 异常的本质
  3. Java异常的设计
  4. Java异常的发生
  5. Java异常的检测
  6. Java异常对象的生成(实例化)
  7. Java异常对象的抛出
  8. Java异常对象的捕获和处理(异常处理器的定义)
  9. 总结

介绍

前面这篇文章介绍JDBC初步使用的时候,我们提到了相关的异常。

现在,我们就来介绍一下Java异常的基础知识,一来以后遇到类似XXXException、throws、throw、try-catch-finally等关键字时能够知道是什么意思;二来我们可以用它来处理或设计我们应用程序中出现的异常。

在某种程度上,可以说编写程序的主要功夫都是花费在异常的设计和处理上。

异常的本质

异常这个词实际上是由英文单词 exception 翻译过来的,其含义是:

n.一般情况以外的人(或事物); 例外; 规则的例外; 例外的事物;

什么是例外?就是正常的情况之外。那什么是正常的情况?可以这么说,在我们程序执行的过程中,你期望它执行的指令流都属于正常的情况。

这样还是很抽象,举个例子,我们的程序是运行在计算机这个设备上的,那么设备肯定会有损坏的时候吧,CPU、内存、硬盘、网卡等等设备都有可能会坏掉,本来程序运行着好好的,突然某个设备坏掉了,这就是一种正常情况之外的事件

再比如,本来程序运行着好好的,这时突然某个设备要求马上占用CPU,那么CPU就要中断正在运行中的程序,去处理该设备的请求,这也是一种正常情况之外的事件。

又比如,本来程序运行着好好的,操作系统或者Java虚拟机的运行出现了故障,那程序是肯定没法继续运行下去了,这也是一种正常情况之外的事件。

又比如,本来程序要读取某个文件中的数据,正常来说,该文件是应该存在的,而谁也不能保证该文件就不会被其他外部力量删除啊,所以,读取文件时遇到文件不存在,或被损坏,或是没有读取权限,或是啥啥啥,这也是一种正常情况之外的事件。

又比如,程序需要执行整数除法,正常情况是不会除以零的,但谁能保证就一定不会出现这种情况呢,CPU是不能执行除零这种运算的,这也是一种正常情况之外的事件。

又比如,你要执行某个对象的方法,而不小心传入的是空(null)引用,这就是著名的NPE(NullPointerException),显然,这是由于我们的错误的使用API造成的,我们不该传入一个空引用,这也是一种正常情况之外的事件。

又比如,一个数组或列表对象,明明没有那么多元素,你却不小心访问超过数组或列表的大小之外的位置,这就是著名的越界异常,这也是一种正常情况之外的事件。

又比如,程序的正常逻辑是希望传入合适的值,或者合适格式的数据,但谁也不能保证就一定能够传入合适的值,或者合适格式的数据啊,这也是一种正常情况之外的事件。

所以,异常的本质就是正常情况之外的事件,当然,你也可以自定义哪些是正常哪些是异常。

当然,有的异常发生后,程序也是无能为力的,比如设备、操作系统、JVM坏了,程序也就随之停止执行了,又如何能够得知并处理它呢。

而有的异常发生后,程序可以捕获它,然后向用户报告发生了什么异常,让用户做出正确选择;或者程序自己处理该异常,自动恢复执行。

Java异常的设计

在Java中,一切都是对象,所以,异常也被设计为对象,这就是异常对象

当然,每一个异常对象都必然属于某个类型,这就是异常类。Java异常类的层次结构如下:

图片来自Java官方文档

每一个Java异常对象,都包含有关于这个异常的信息,比如,这个异常是在执行到哪个类的哪个方法的哪行代码时发生的;当然方法的调用会形成调用栈(即一个方法中又会调用自己或其他的方法),所以整个调用栈的信息也是很重要的,以后再细说;还有,有时候一个异常是由另一个异常引起的,这又会形成异常链,这也是很重要的信息。

  • Error类:就是下面所说的严重的异常,一般是我们程序的外部发生的;
  • Exception类:一般是我们程序的内部发生的,又分为RuntimeException类和其他类;
  • RuntimeException类:是属于内部异常,但通常也是不可预料和不可恢复的,通常预示着程序有某种bug,比如逻辑错误或者API的错误使用等。

其中,Error和RuntimeException都属于不可预料和不可恢复的,统称为 unchecked exceptions (可以翻译为非受检异常),剩下的自然就是受检异常了。

为什么叫受检和非受检呢?从形式上看,一旦一个方法内部可能会抛出受检异常,就必须检查它:

  • 使用 try-catch 块定义异常处理器来处理该异常;
  • 或者在该方法的声明时使用 throws 关键字指明该方法不处理该异常,进一步抛出该方法的调用者,由JVM去检测该调用者是否处理该异常。

否则,Java编译器会编译报错!

我们通常设计自己的异常类时,首先必须处于上面Throwable类层次结构中,但一般都会设计为继承Exception类或其子类。

Java异常的发生

异常的种类很多,但从发生来源看,就只有两种:

  • 我们程序外部产生的,比如硬件设备、操作系统、Java虚拟机等,通常,这种异常被封装成Error类及其子类,从类名就可以看出这不仅仅是一个异常,更是一个错误,比起异常来更加严重的,一般也是我们不能预料的,不能恢复的;
  • 我们程序内部产生的,即执行我们的程序代码所产生的异常,它们被Java设计为Exception类及其子类。

所以说,异常这个名字起的有点不太合适,或者说把Exception作为父类的名字再设计ErrorXXXException作为它的子类更合适一点。

不管怎样,我们还是适应现状吧。

Java异常的检测

不管是Error,还是Exception,我们的系统必须要检测到它的发生。

因为Java程序是运行在JVM中的,显然JVM可以检测到异常的发生,比如,IO异常、除零、空指针、越界异常等都可以被JVM检测到。

当然,我们的程序也可以检测异常,甚至是上面的异常,比如,在读取文件时可以先判断文件是否存在、做整数除法时先判断除数是否为零、访问某对象的方法时先判断该引用是否为空、访问数组或列表时先判断索引是否越界等。

不过,我们显然不必多此一举,上面的异常都太普遍,程序的任何地方都可能会发生,如果每一处都要我们检测,那我们的代码就到处充斥着检测的代码。

我们的程序通常检测的是我们自定义的不具备普遍性的异常,比如指定某些变量必须是业务逻辑范围内的,某些数据的格式必须是我们指定的格式等等。

所以,通常检测代码都是判断一些正常的条件是否没有满足,或者一些异常的条件是否满足。

JVM中检测异常的代码我们自然是看不到了(实际上也能够看到,那就是查看JVM的实现代码),不过肯定是在执行Java程序的地方增加了检测异常的代码。所以,我们在编写Java程序的时候,不需要增加任何代码,JVM也能够检测到那些普遍性的异常,比如:

public void m(Object obj) { obj.toString();}

上面代码中,一旦在调用该方法时传入一个null值,JVM在执行 obj.toString() 自然会检测到空指针异常(NPE)。

当然,JVM除了检测异常,还会生成异常对象并抛出,捕获该异常对象,寻找一个匹配的异常处理器(如果找不到则使用默认的异常处理器),执行异常处理器的代码(处理异常)等。

Java异常对象的生成(实例化)

既然Java异常的发生可以由JVM和我们的应用程序所检测到,那Java异常对象的生成自然也就由它们来生成了。

JVM生成异常对象的方式不敢说就是使用 new 操作,但估计也是类似,不过可能使用的是原始的JVM指令。

我们的应用程序生成异常对象的方式,与生成普通对象的方式一样,可以使用 new 操作:

XXXException ex = new XXXException(parameters);

Java异常对象的抛出

Java为我们设计了一种异常对象的抛出机制

既然是抛出,那是谁抛出呢?显然是发生异常的那句代码抛出啊,不过,Java代码都是组织成类中的方法,所以也可以说是包含发生异常的那句代码的方法抛出异常

事实上,Java语言和JVM就是这么设计的,一个方法可以声明抛出某个异常(受检和非受检均可,但方法内如果有未定义异常处理器的受检异常,则必须声明),使用的是 throws 关键字,像下面这样:

public void m() throws XXXException, YYYException, ...

当然,可以声明抛出多个异常,以逗号分隔即可。

既然声明一个方法可能抛出异常,那么该方法肯定是包含可能发生异常的代码。如果该代码由JVM检测并生成的异常对象,当然就由它来抛出了,我们不必深究,比如:

public void m(Object obj) { obj.toString();//可以理解为,这里由JVM隐式的检测异常、生成异常对象并抛出了 //下面是其他代码,如果上面抛出异常,则不会执行了}

如果是我们的应用程序中检测到某种异常并生成异常对象,就由我们来显式的抛出,使用Java提供给我们的 throw 语句:

if (发生异常) { throw new XXXException();}//下面是其他代码,如果上面抛出异常,则不会执行了

上面的代码包括了检测异常的发生、异常对象的生成并抛出。

那又是抛给谁呢?显然是抛给JVM啊,一旦抛出异常对象,后面的代码就不会再执行了,JVM会拿该异常对象去寻找匹配的异常处理器了。

所以,抛出机制本质上是一种(CPU的)控制权的转移,是在我们的程序中各个方法和JVM之间转移,转移的目的是寻找到匹配的异常处理器,而转移的数据就是那个异常对象!

Java异常对象的捕获和处理(异常处理器的定义)

既然Java异常对象是抛给JVM,显然JVM就捕获到了这个异常对象,但这个捕获显然很不够,必须将该异常对象交给某段代码来处理才行。

这个某段代码就是异常处理器。

Java为我们提供了定义异常处理器的 try-catch-finally 块语法:

try { //这里是可能抛出异常的代码 ... obj.m(); //这里可能隐式的抛出NPE,或者m()方法声明会抛出某种异常 if (发生异常) { throw new XXXException(); //这里显式的抛出自定义的或第三方库提供的异常 } //后面的代码 ...} catch (XXXException e) { //这里就定义了一个异常处理器,它匹配抛出的异常类型是XXXException} catch (YYYException e) { //这里又定义了一个异常处理器,它匹配抛出的异常类型是YYYException} finally { //这里的代码无论try块中是否抛出异常,都会执行,除非JVM退出或执行try块的线程销毁}

try-catch-finally 块语法必须至少包括一个catch语句或一个finally语句,可以包括多个catch语句。

需要注意的是,finally块并没有定义一个异常处理器,它是无论是否发生异常都会执行的代码,一般是用于编写释放资源的代码。

事实上,异常对象被抛出后,JVM就获得了控制权,它会寻找匹配的异常处理器,首先会在本方法中寻找,如果没有,就到该方法的调用者内去寻找,一直找到最顶层方法为止,如果最终都没有找到,那就会交给默认的异常处理器。

所以可以这么说,最后是某个异常处理器捕获到了该异常对象。

总结

  • 所谓异常,就是正常情况之外的事件,异常可以是系统引发的,也可以是业务自定义的;
  • 异常,从生命周期思维看,会经历发生、检测、处理(此阶段在Java中又分为异常对象的生成、抛出、捕获和处理)等阶段;
  • Java里面将异常封装成类,形成类层次结构,顶层父类是Throwable;
  • Java中的异常分为三种:Error及其子类、RuntimeException及其子类、其他Exception及其子类,最后一种是受检异常,方法内必须为它定义异常处理器或方法中声明要抛出它;前两种是非受检异常,往往是严重的、普遍存在的、不可预料且不可恢复的;
  • JVM和我们的应用程序都可以进行异常的检测、异常对象的生成、异常对象的抛出、异常对象的捕获和处理(异常处理器定义、匹配、执行);
  • 异常对象的生成可以使用 new 操作符,与普通对象的实例化无异;
  • 异常对象的抛出在方法声明中可以使用 throws 关键字;在方法内部可以使用 throw 关键字;
  • 异常处理器的定义使用 try-catch-finally 块语法,每一个catch就是定义了一个异常处理器;
  • 异常处理器的匹配和执行是由JVM执行的,匹配主要是依据异常的类型,在方法的调用栈中依次寻找匹配的异常处理器;
  • 异常可以形成异常链,即异常处理器捕获到一个异常后,生成另一个异常继续抛出;
  • 异常的运行机制可以看作是一种控制权的转移机制(事实上,所有应用程序与系统之间也是如此),在Java中则是(CPU的)控制权在应用程序与JVM之间转移,我们可以把(运行中的)JVM想象成是一个人(实际上应该把CPU比作一个人,但抽象是可以有层级的,这样也更易理解一些),Java程序则是一条条指令,这个人在不断的读取指令并执行,这些指令肯定有一定的语法,碰到某些指令就得执行某些动作:

这个人先寻找main方法,每遇到一个方法的调用,就会去寻找该方法的代码,并记录到方法调用栈;每遇到一个try-catch块就记录该方法定义了一个异常处理器;一旦某个方法内抛出异常,就寻找匹配的异常处理器;该方法没有匹配的异常处理器就继续往它的调用者方法寻找,直到找到有匹配的异常处理器,并执行该异常处理器的代码,然后继续从该异常处理器之后的代码开始执行。

java 抛出异常_我的Java Web之路51 - Java异常基础相关推荐

  1. 选课系统java源文件_学生选课系统 - WEB源码|JSP源码/Java|源代码 - 源码中国

    压缩包 : 选课系统代码+论文.rar 列表 选课系统代码+论文\test\AddClass.jsp 选课系统代码+论文\test\Addcourse.jsp 选课系统代码+论文\test\addst ...

  2. java定义list_我的Java Web之路59 - Java中的泛型

    本系列文章旨在记录和总结自己在Java Web开发之路上的知识点.经验.问题和思考,希望能帮助更多(Java)码农和想成为(Java)码农的人. 目录 介绍 再谈Java中的类型 为什么需要泛型? J ...

  3. 反转字符串java实现_反转字符串的几种实现(Java)

    反转字符串的几种实现(Java) 首先第一种是利用Java中的类库对象进行反转 //第一种 使用Java类库的diam实现反转 public String reverse(String str){ S ...

  4. aws部署java应用_在 Elastic Beanstalk 上创建和部署 Java 应用程序 - AWS Elastic Beanstalk...

    本文属于机器翻译版本.若本译文内容与英语原文存在差异,则一律以英文原文为准. 在 Elastic Beanstalk 上创建和部署 Java 应用程序 AWS Elastic Beanstalk 支持 ...

  5. 知识图谱java实现_知识图谱:neo4j(四)Java API

    知识图谱:neo4j(四)Java API 知识图谱:neo4j(四)Java API Neo4j Java API Neo4j 提供 JAVA API 以编程方式执行所有数据库操作.它支持两种类型的 ...

  6. dofilter在java中_在Filter的doFilter中进行重定向 出现异常

    我在 filter的 dofilter方法里 重定向到一个servlet 出现java.lang.IllegalStateException: Cannot create a session afte ...

  7. 心java源代码_写了一个心形图案的java源代码,想发给朋友,朋友怎样才能像打开文档一样方便查看呢?...

    搞清楚java的运行机制你就明白了该怎么办 java源代码->java编译器生成字节码文件->java虚拟机执行字节码文件->心形图案显示 因此你要让朋友直接运行,你就要让上述过程自 ...

  8. 红米2支持java吗_红米2a会有报错:java.lang.NullPointerException

    离线打包版本用的是2016年3月16日发布的. 其他手机正常,目前只有红米2a会有报错:java.lang.NullPointerException log信息如下: java.lang.Runtim ...

  9. idea14创建java项目_使用IntelliJ IDEA 14和Maven创建java web项目

    安装Maven 下载安装 去maven官网下载最新版. 解压到安装目录. 配置 右键桌面的计算机图标,属性–>高级系统设置–>环境变量,添加M2_HOME的环境变量,然后将该变量加入的PA ...

最新文章

  1. 海量数据处理相关面试问题
  2. 简单工厂、工厂模式初学习
  3. python图片识别-用10行Python代码进行图像识别
  4. PXC5.7(Percona XtraDB Cluster)+HAproxy集群部署
  5. ProgressDialog 一个使用类,多多提建议
  6. 10亿 !清华大学又获巨额捐赠
  7. ERROR in static/js/vendor.js from UglifyJs UUnexpected token: name (Dom7)
  8. Soft NMS算法笔记
  9. RAID简单介绍和Linux如何搭建一个RAID
  10. Linux 命令之 crontab 计划任务与自动同步系统时间
  11. Datalogic得利捷Memor™ 10入选“安卓企业推荐计划”
  12. 教你认识正斜杠(/)与反斜杠(\)
  13. 利用python实现判断两条直线是否平行,若相交,输出交点。
  14. 全美“50大好差事” 软件工程师排名第一
  15. 摩羯座|摩羯座性格分析
  16. AngularJS 表达式
  17. 产品经理 - 产品设计方法论业务落地部分_包括流程产品文档方法论需求设计方法论
  18. 西门子PLC之间如何建立无线通讯?
  19. mysql创建部门表和员工表,并用sql语句进行查询
  20. 物联网恶意软件“Mirai”源代码被黑客公开 绿盟科技分析报告开放下载

热门文章

  1. 最令人讨厌的编程语言:C++ Java 上榜
  2. 后 5G 时代,路在何方?
  3. 腾讯优图开源深度学习推理框架 TNN,助力 AI 开发降本增效
  4. 这五个有用的 CSS 属性完全被我忽视了
  5. 程序员惊魂 12 小时:“���”引发线上事故
  6. DDoS 攻击爆发!医疗在线教育成重点,代理攻击成常态
  7. ofo 退押金出新招;快手回应“弃拼多多联手阿里”;Julia 1.3 发布 | 极客头条...
  8. 我有一辆机器人小车,怎么让它跑起来,还会避障、目标跟踪、路径规划?
  9. 爆炸了!刚刚!小姐姐说她找了个程序员男友
  10. OpenStack 已死?