文章目录

  • 什么是异常
  • 异常分类
  • 初识异常
  • 捕获异常
    • try...catch...finally
    • throws
    • throw
  • 抛出异常的过程
  • 异常包装

什么是异常

程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?Java 提供了更加优秀的解决办法:异常处理机制。

异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。

Java 中的异常可以是函数中的语句执行时引发的,也可以是程序员通过 throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE 就会试图寻找异常处理程序来处理异常。

Throwable 类是 Java 异常类型的顶层父类,一个对象只有是 Throwable 类的(直接或者间接)实例,它才是一个异常对象,才能被异常处理机制识别。JDK 中内建了一些常用的异常类,我们也可以自定义异常。

异常分类


1、所有的异常都是从 Throwable 继承而来的,是所有异常的共同祖先。

2、Throwable 有两个子类,ErrorException

其中 Error 是错误,对于所有的编译时期的错误以及系统错误都是通过 Error 抛出的。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如 Java 虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。一般发生这种异常,JVM 会选择终止程序。因此我们编写程序时不需要关心这类异常。

Exception 它规定的异常是程序本身可以处理的异常。异常和错误的区别是,异常是可以被处理的,而错误是没法处理的。

3、Exception 类的异常包括checked exceptionunchecked exception

Checked Exception即可检查的异常,都是需要在代码中处理的。它们的发生是可以预测的,比如 IOException,或者一些自定义的异常。除了 RuntimeException 及其子类以外,都是 checked exception。必须对该异常进行处理,要么使用 try-catch 捕获,要么使用 throws 语句抛出,否则编译不通过。

Unchecked Exception包括 RuntimeException 及其子类都。比如空指针异常,除数为0的算数异常 (ArithmeticException) 等等,这种异常是运行时发生,无法预先捕捉处理的。Error 也是 unchecked exception,也是无法预先处理的。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。

初识异常

封装错误信息的对象包括:类名、错误信息、行号

下面的代码会演示几个异常类型

ArrayIndexOutOfBoundsException 下标越界

public class Main {public static void main(String[] args) {f();}private static void f() {System.out.println("输入逗号隔开的两个整数");String s = new Scanner(System.in).nextLine();String[] a = s.split(",");int n1 = Integer.parseInt(a[0]);int n2 = Integer.parseInt(a[1]);System.out.println(n1 / n2);}
}

运行程序,程序要求我们输入用逗号隔开的两个整数,我们只输入一个后会报错

类型:java.lang.ArrayIndexOutOfBoundsException(下标越界)
错误信息:index 1 out of bounds(a[1]没有)
行号:Main.java 16行(int n2 = Integer.parseInt(a[1]);)

NumberFormatException 数字格式错误

类型:java.lang.NumberFormatException(数字格式错误)
错误信息:abc(abc不是数字)
行号:Main.java:15(int n1 = Integer.parseInt(a[0]);)

ArithmeticException 异常的运算条件


类型:java.lang.ArithmeticException(异常的运算条件)
错误信息:/ by zero(除数为0)
行号:Main.java:17(System.out.println(n1 / n2);)

上面的代码不使用异常处理机制,也可以顺利编译,因为3个异常都是非检查异常。

在下面捕获异常一节中的例子就必须使用异常处理机制,因为异常是检查异常。

捕获异常

事实上,程序崩溃是件体验非常不好的事情,因此,我们可以让异常不穿过 main() 方法。我们可以接住这个异常,并且处理它。这个接住异常的过程称为捕获异常。我们先看栗子。

我们修改上面的代码

public class Main {public static void main(String[] args) {while (true) {try {//f()方法出现异常,直接捕获异常f();//如果f()方法正常执行完,执行break跳出循环break;} catch (ArrayIndexOutOfBoundsException e) {System.out.println("两个整数,不是1个");} catch (NumberFormatException e) {System.out.println("输入整数");} catch (Exception e) {System.out.println("出错请重试!");} finally {System.out.println("~~~~~~~~~~");}}System.out.println("出异常就不会执行");}private static void f() {System.out.println("输入逗号隔开的两个整数");String s = new Scanner(System.in).nextLine();String[] a = s.split(",");int n1 = Integer.parseInt(a[0]);int n2 = Integer.parseInt(a[1]);System.out.println(n1 / n2);}
}

运行结果:

异常处理的基本语法
在编写代码处理异常时,对于检查异常,有 2 种不同的处理方式:
① 使用try...catch...finally语句块处理它。
② 在函数签名中使用throws声明交给函数调用者caller 去解决。

try…catch…finally

     try {//try块中放可能发生异常的代码。//如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。//如果发生异常,则尝试去匹配catch块。} catch (AException e) {//每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。//catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。//在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。//如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。//如果try中没有发生异常,则所有的catch块将被忽略。} catch (BException e) {//捕获B类型异常} catch (父类型Exception e) {//其他异常可以直接捕获父类型异常//因为多个异常,只会捕获一个,后边的捕获不会执行,所以父类型要放到最后//父类型放到开始,执行完后边的异常捕获就不执行了} finally {//finally块通常是可选的。//无论出不出错,都会执行//捕获了一个异常,会执行//正常执行了程序,会执行//一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。//finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 }

需要注意的地方
1、try 块中的局部变量和 catch 块中的局部变量(包括异常变量),以及 finally 中的局部变量,它们之间不可共享使用。

2、每一个 catch 块用于处理一个异常。异常匹配是按照 catch 块的顺序从上往下寻找的,只有第一个匹配的 catch 会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个 try 块下的多个 catch 异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个 catch 块都有存在的意义。

3、java 中,异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说:当一个函数的某条语句发生异常时,这条语句的后面的语句不会再执行,它失去了焦点。执行流跳转到最近的匹配的异常处理 catch 代码块去执行,异常被处理完后,执行流会接着在"处理了这个异常的 catch 代码块"后面接着执行。

4、良好的编程习惯是:在 try 块中打开资源,在 finally 块中清理释放这些资源。

有的编程语言当异常被处理后,控制流会恢复到异常抛出点接着执行,这种策略叫做:resumption model of exception handling(恢复式异常处理模式 )
而 Java 则是让执行流恢复到处理了异常的 catch 块后接着执行,这种策略叫做:termination model of exception handling(终结式异常处理模式)

throws

throws 声明:如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,则 javac 保证你必须在方法的签名上使用 throws 关键字声明这些可能抛出的异常,否则编译不通过。

throws 是另一种处理异常的方式,它不同于try...catch...finally,throws,仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。

采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。

写如下代码,输入生日日期,创建一个时间戳名称的 txt 文件,保存到 D 盘:

现在代码是报错状态,因为 parse 这个方法设置了异常抛出的管道,f() 方法应该做相应的处理,可以设置相应的异常抛出管道,或者捕获异常。

查看需要设置的异常抛出管道,点击 Add Exception to method signature,自动添加


throws 后面用逗号隔开可以设置多个管道,添加完之后,f() 方法处报错,因为刚才处理的两个管道不是 RuntimeException,所以需要添加管道或者捕获异常

我们这里增加 try…catch

public class Main {public static void main(String[] args) {try {f();} catch (ParseException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}private static void f() throws ParseException, IOException {System.out.println("输入生日");String s = new Scanner(System.in).nextLine();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date d = sdf.parse(s);File f = new File("d:/"+d.getTime()+".txt");f.createNewFile();}
}

如果输入格式不正确,运行结果

如果在代码中将文件保存到不存在的盘符:“k:/”,运行结果

throw

程序员可以通过 throw 语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。

throw 语句必须写在函数中,执行 throw 语句的地方就是一个异常抛出点,它和由JRE 自动形成的异常抛出点没有任何差别。

看以下代码:

public class Main {public static void main(String[] args) {f();}private static void f() {System.out.println("输入两个浮点数:");Double a = new Scanner(System.in).nextDouble();Double b = new Scanner(System.in).nextDouble();System.out.println(divide(a, b));}private static double divide(double a, double b) {return a / b;}
}

正常情况下程序运行结果

如果除数是 0,运行结果:

程序不会报错,但会返回 Infinity,这时可以手动创建一个异常对象,我们只需修改 divide 方法:

private static double divide(double a, double b) {//手动创建异常对象if (b == 0) {//可以加提示消息,给程序猿看throw new ArithmeticException("/ by zero");}return a / b;}

当除数再次输入 0:

手动添加了异常对象后,还可以在调用方法的地方捕获异常,来提示用户,完整代码如下

public class Main {public static void main(String[] args) {f();}private static void f() {System.out.println("输入两个浮点数:");Double a = new Scanner(System.in).nextDouble();Double b = new Scanner(System.in).nextDouble();//可以在这里捕获异常try {System.out.println(divide(a, b));} catch (ArithmeticException e) {//提示用户System.out.println("不能除0是我们的错,请鞭挞我们");//打印错误信息,给程序猿看e.printStackTrace();}}private static double divide(double a, double b) {//手动创建异常对象if (b == 0) {//可以加提示消息,给程序猿看throw new ArithmeticException("/ by zero");}return a / b;}
}

运行结果

抛出异常的过程

我们来分析下上面的程序抛出异常的过程:
1、main() 方法中调用 f() 方法
f() 中产生的错误会封装成一个对象传递给 main() 方法
f() 方法产生错误后的代码不再执行,返回 main() 方法
这个动作叫做抛出

2、如果 main() 不做处理,会穿过 main() 方法,抛给虚拟机

3、虚拟机会打印异常信息,并且退出

4、虚拟机退出,程序直接崩溃

异常包装

捕获一个异常对象,把它包装在另一种类型异常对象中,再抛出

为什么包装
不能抛出的异常,包装成能抛出的异常,再抛出
多种类型异常包装成一种异常,再抛出,简化异常

栗子

public class Main {public static void main(String[] args) {f();}private static void f() {List<String> list = new ArrayList<>();Collections.addAll(list, "2015-8-15", "2012-9-15", "2021-9-28");System.out.println(list);Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String arg0, String arg1) {SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");try {Date d1 = f.parse(arg0);Date d2 = f.parse(arg1);return d1.compareTo(d2);} catch (ParseException e) {throw new RuntimeException(e);}}});System.out.println(list);}
}

运行结果

代码中,f.parse(arg01);f.parse(arg02); 有 ParseException 这样一个管道,现在必须二选一,要么 try…catch,要么抛出异常。

然后 throws ParseException 是不行的,如果有两个类,父类中有一个方法,子类重写这个方法,父类的异常管道是个上限,子类添加的异常管道不能比父类多,而Comparator 的方法 compare 只有一个默认管道 RuntimeException,所以子类重写这个方法不能增加一个管道,比父类多,所以只能 try…catch

而 catch 中没有办法修复这个错误,返回正数说明,arg0大,返回负数说明,arg0小,返回 0 说明相等,这样都不合适,所以依然在 catch 中抛出异常

Java中的异常和处理详解

【达内课程】异常Exception(上)相关推荐

  1. 我的女朋友漏电了–论C++中的失败(failure),缺陷(bug)和异常(exception)

    先做个广告置入,如果喜欢这篇文章,你可以到 zhaoyan.website/blog 去查看于此类似的C/C++文章. 我承认有点标题党了,不过这真的是一篇写软件的文章,所以如果你已经抽出了一张面巾纸 ...

  2. Windows核心编程 第2 5章 未处理异常和C ++异常(上)

    未处理异常和C + +异常(上) 前一章讨论了当一个异常过滤器返回 E X C E P T I O N _ C O N T I N U E _ S E A R C H时会发生什么事情.返回EXCEPT ...

  3. Java中的异常 Exception

    Java中的异常 Exception java.lang.Exception类是Java中所有异常的直接或间接父类.即Exception类是所有异常的根类. 比如程序: public class Ex ...

  4. 报错空指针异常_让你为之颤抖的Java常见的异常exception

    对于一名Java程序员来说,exception再常见不过了.工作时,一天不见便浑身难受.当然了,如果辛辛苦苦敲了一天的代码,就要下班了,信心满满,代码跑完收工.这个时候控制台告诉你:......... ...

  5. java经常会出现异常的是,“Java异常Exception”总结

    1. 异常(Exception). 2.Java中的异常分为两大类: a) Checked exception  (非  Runtime Exception) 非运行时异常 b) Unchecked ...

  6. ASP.NET企业开发框架IsLine FrameWork系列之九--ExceptionProcessProvider异常框架(上)

    ASP.NET企业开发框架IsLine FrameWork系列之九--ExceptionProcessProvider异常框架(上) 接上文 IsLine.ExceptionProcess.Excep ...

  7. java中exception作用_java中的异常Exception的划分,以及他们的特性。

    1.异常分为编译时异常(ExceptionSubclass)和运行时异常(RuntimeException): 2.编译时异常的发生概率高,而运行时异常的发生概率比较低,因此编译时在编写代码时应该处理 ...

  8. classcastexception异常_让你为之颤抖的Java常见的异常exception

    对于一名Java程序员来说,exception再常见不过了.工作时,一天不见便浑身难受.当然了,如果辛辛苦苦敲了一天的代码,就要下班了,信心满满,代码跑完收工.这个时候控制台告诉你:......... ...

  9. Java异常的根类似_Java异常(Exception)类型及处理

    Java中的异常全部都是以对象形式存在的,一旦某句代码发生异常,会在该代码处生成一个异常对象,然后以堆栈式抛出,若不对该异常对象进行处理,最终导致程序终止运行. Java语言异常分类 编译时异常(受检 ...

  10. Java 里的异常(Exception)详解

    作为一位初学者, 本屌也没有能力对异常谈得很深入.   只不过java里关于Exception的东西实在是很多. 所以这篇文章很长就是了.. 一, 什么是java里的异常 由于java是c\c++ 发 ...

最新文章

  1. 3D目标检测论文阅读多角度解析
  2. 人工智能可以发现数据中隐藏的物理规律
  3. SQL语句——将Excel文档导入数据表中
  4. eclipse - unresolved inclusion: stdio.h
  5. 洛谷 - P4173 残缺的字符串(多项式匹配字符串-NTT)
  6. python拟牛顿法迭代点绘制_最速下降法、牛顿法、拟牛顿法,Python实现高维二次目标函数优化...
  7. redis服务器学习一
  8. 计算机应用基础 辅助教学系统,计算机应用基础课程辅助教学及智能测评系统使用手册——网络版.docx...
  9. JavaScript——闭包函数及拓展题目
  10. android修改尾巴软件,安卓手机QQ自定义尾巴编辑教程
  11. Python求梅森尼数
  12. linux命令(日常更新)
  13. 苹果id解锁平台_黄蜂2单手手柄体验:苹果安卓平台双通用,解锁新潮玩法!
  14. linux flash 存储寿命,关于 Flash 存储,你应该知道的一些事情
  15. Windows XP SP3 下 High Definition Audio 声卡安装方法
  16. vector用erase删除元素时,为什么被删元素的析构函数会被调用更多次?
  17. python3GUI--打造一款音乐播放器By:PyQt5(附下载地址)
  18. excel文件损坏修复绝招_优盘显示文件或目录已损坏,无法读取,怎么办?!
  19. 软件工程第一次作业——制作个人简介
  20. kubeadm部署k8s多master节点的高可用集群

热门文章

  1. CreateJS 入门小记
  2. 如何录制电脑内部声音 视频中的音频怎么录制
  3. 腾讯wifi管家竟然盗取我家wifi密码 惊讶!震惊!失望!
  4. 基于SSM实现企业生资源管理系统-ERP系统
  5. 福布斯30岁以下30强名单新出炉!区块链从业者占比13%
  6. 【Java】log日志输出
  7. 如何制作自己的系统镜像以及备份管理
  8. 计算机社团教学活动总结感悟,社团活动收获与感悟【四篇】
  9. 介绍旅游网站建设与优化技巧
  10. 【洛谷P3386】【模板】二分图匹配【网络流】