志达IT
做快乐程序员

java的异常分为哪两类(java的异常处理机制)

java的异常分为哪两类

1、java中反常的两种处理办法有,对代码块用trycatch进行反常捕获处理,在代码的办法体外用throws进行抛出声明,奉告此办法的调用者这段代码可能会出现反常,慎重处理,如果声明抛出的反常是非运行时反常。
2、办法的调用者显示地用trycatch块进行捕获或者继续向上层抛出反常,如果声明抛出的反常是运行时反常,办法的调用者可以挑选地进行反常捕获处理。
3、在代码块用throw手动抛出一个反常目标,此时也有两种情况跟2中的类似,如果抛出的反常目标是非运行时反常,此办法的调用者显示地用trycatch块进行捕获或者继续向上层抛出反常。

java的异常处理机制

Java 言语在规划之初就提供了相对完善的反常处理机制。
咱们首先介绍一下 Java 中的反常。
介绍 Java 中的反常
反常是程序在运转过程中呈现的程序反常事情,反常会中止正在执行的正常指令流 。
Java 中的反常分为两大类:Exception 和 Error。
下面是 Exception 和 Error 的类界说
public class Exception extends Throwable {}public class Error extends Throwable {}
Exception 和 Error 都继承了 Throwable 类,在 Java 中只要 Throwable 类型的实例才可以被抛出(throw)或许被捕获(catch)。
Exception 和 Error 表现了 Java 平台规划者对不同反常状况的分类。
下面咱们逐一介绍 Error 和 Exception。java
介绍 Error
Error 类目标一般是由虚拟机生成并抛出,绝大部分的 Error 都会导致虚拟机本身处于不行康复的状况,是程序无法控制和处理的。当呈现 Error 时,一般会挑选停止线程。
Error 中最常见的是虚拟机运转过错(VirtualMachineError 抽象类)。
虚拟机运转过错中最常见的有:
内存溢出(OutOfMemoryError):由于内存不足,虚拟机没有可分配的内存了,垃圾回收器也不能开释更多的内存,那么虚拟机抛出 OutOfMemoryError
栈溢出(StackOverflowError):假如一个线程已用的栈巨细 超过 配置的答应最大的栈巨细,那么虚拟机抛出 StackOverflowError
介绍 Exception
Exception 有两种类型「编译时反常」和「运转时反常」
「编译时反常」对应 Java 的 Exception 类
「运转时反常」对应 Java 的 RuntimeException 类(RuntimeException 类继承 Exception 类 )
下面是 Exception、RuntimeException 类的界说
public class Exception extends Throwable {}public class RuntimeException extends Exception {}
关于「运转时反常」,咱们在编写代码的时分,可以不用主动去 try-catch 捕获(不强制要求),编译器在编译代码的时分,并不会查看代码是否有对运转时反常做了处理。
相反,关于「编译时反常」,咱们在编写代码的时分,有必要主动去 try-catch 获取 或许 在函数界说中声明向上抛出反常(throws),否则编译就会报错。
所以:
「运转时反常」也叫作非受检反常(Unchecked Exception)
「编译时反常」也叫作受检反常(Checked Exception)
在函数抛出反常的时分,,咱们该怎样处理呢?是吞掉仍是向上抛出?
假如挑选向上抛出,咱们应该挑选抛出哪种类型的反常呢?是受检反常还对错受检反常?
咱们下文会对此介绍。
常见的编译时反常有:
FileNotFoundException:当尝试打开由指定路径表明的文件失败时抛出
ClassNotFoundException:当应用程序尝试经过其字符串称号加载类时抛出,以下三种办法加载
Class.forName(java.lang.String)
ClassLoader.findSystemClass(java.lang.String)
ClassLoader.loadClass(java.lang.String, boolean)
常见的运转时反常有:
不合法参数反常(IllegalArgumentException):当传入了不合法或不正确的参数时抛出
空指针反常(NullPointerException):当在需求目标的状况下运用了 null 时抛出。
下标拜访越界反常(IndexOutOfBoundsException):当某种索引(例如数组,字符串或向量)的索引超出范围时抛出。
类型转化反常(ClassCastException):当尝试将目标转化为不是实例的子类时抛出。
运算反常(ArithmeticException):运算条件呈现反常时抛出。例如,“除以零”的整数。
Java 反常类的结构
怎样处理函数抛出的反常
在函数抛出反常的时分,咱们该怎样处理呢?是吞掉仍是向上抛出?
假如挑选向上抛出,咱们应该挑选抛出哪种类型的反常呢?是受检反常还对错受检反常?
下面咱们就对此介绍。
吞掉 or 抛出
在函数抛出反常的时分,咱们该怎样处理?是吞掉仍是向上抛出?
总结一下,在函数抛出反常的时分,一般有下面三种处理办法。
直接吞掉
原封不动地 re-throw
包装成新的反常 re-throw
直接吞掉。详细的代码示例如下所示:
public void func1() throws Exception1 { // …}public void func2() { //… try { func1(); } catch (Exception1 e) { //吐掉:try-catch打印日志 log.warn(“…”, e); } //…}
原封不动地 re-throw。详细的代码示例如下所示:
public void func1() throws Exception1 { // …}//原封不动的re-throw Exception1public void func2() throws Exception1 { //… func1(); //…}
包装成新的反常 re-throw。详细的代码示例如下所示:
public void func1() throws Exception1 { // …}public void func2() throws Exception2 { //… try { func1(); } catch (Exception1 e) { // wrap成新的Exception2然后re-throw throw new Exception2(“…”, e); } //…}
当咱们面临函数抛出反常的时分,应该挑选上面的哪种处理方式呢?我总结了下面三个参考原则:
假如 func1() 抛出的反常是可以康复,且 func2() 的调用方并不关怀此反常,咱们完全可以在 func2() 内将 func1() 抛出的反常吞掉;
假如 func1() 抛出的反常对 func2() 的调用方来说,也是可以了解的、关怀的 ,并且在事务概念上有必定的相关性,咱们可以挑选直接将 func1 抛出的反常 re-throw;
假如 func1() 抛出的反常太底层,对 func2() 的调用方来说,缺少背景去了解、且事务概念上无关,咱们可以将它从头包装成调用方可以了解的新反常,然后 re-throw。
应该挑选上面的哪种处理方式,总结来说就是从以下两个方面进行判别:java
函数1 抛出的反常是否可以康复
函数1 抛出的反常关于 函数2 的调用方来说是否可以了解、关怀、事务概念相关
总归,是否往上持续抛出,要看上层代码是否关怀这个反常。关怀就将它抛出,否则就直接吞掉。
是否需求包装成新的反常抛出,看上层代码是否能了解这个反常、是否事务相关。假如能了解、事务相关就可以直接抛出,否则就封装成新的反常抛出。
关于处理函数抛出的反常,咱们需求注意:
假如挑选吞掉函数抛出的反常的话,咱们有必要把反常输出到日志体系,方便后续确诊。
假如把反常输出到日志体系时,咱们在确保确诊信息满足的一起,也要考虑防止包括灵敏信息,由于那样或许导致潜在的安全问题。
假如咱们看 Java 的规范类库,你或许注意到相似 java.net.ConnectException,犯错信息是相似“ Connection refused (Connection refused)”,而不包括详细的机器名、IP、端口等,一个重要考量就是信息安全。
相似的状况在日志中也有,比方,用户数据一般是不行以输出到日志里面的。
受检反常 or 非受检反常
在函数抛出反常的时分,假如挑选向上抛出,咱们应该挑选抛出哪种类型的反常呢?是受检反常还对错受检反常?
关于代码 bug(比方下标拜访越界、空指针)以及不行康复的反常(比方数据库连接失败),即使咱们捕获了,也做不了太多事情,咱们希望程序能 fail-fast,所以,咱们倾向于运用非受检反常,将程序停止掉。
关于可康复反常、事务反常,比方提现金额大于余额的反常,咱们更倾向于运用受检反常,明确告知调用者需求捕获处理。
处理反常的原则
尽量不要捕获通用反常
下面举例说明,实例代码如下:
try { // 事务代码 // … Thread.sleep(1000L);} catch (Exception e) { // Ignore it}
关于 Thread.sleep() 函数抛出的 InterruptedException,咱们不应该捕获 Exception 通用反常,而应该捕获 InterruptedException 这样的特定反常。
这是由于咱们要确保程序不会捕获到咱们不希望捕获的反常。比方,咱们更希望 RuntimeException 导致线程停止,而不是被捕获。
不要生吞反常
不要生吞(swallow)反常,尽量把反常信息记录到日志体系中。
这是反常处理中要特别注意的事情,由于生吞反常很或许会导致难以确诊的诡异状况。
假如咱们没有把反常抛出,也没有把反常记录到日志体系,程序或许会在后续呈现难以排查的 bug。没人可以轻易判别究竟是哪里抛出了反常,以及是什么原因产生了反常。
再来看一段代码
try { // 事务代码 // …} catch (IOException e) { e.printStackTrace();}
这段代码作为一段实验代码,是没有任何问题的,可是在产品代码中,一般都不答应这样处理。
你先思考一下这是为什么呢?
咱们先来看看 printStackTrace() 的文档,开头就是“Prints this throwable and its backtrace to the standard error stream”。问题就在这里,在稍微复杂一点的生产体系中,规范犯错(STERR)不是个合适的输出选项,由于你很难判到底输出到哪里去了。尤其是关于分布式体系,假如产生反常,可是无法找到堆栈轨迹(stacktrace),这纯属是为确诊设置障碍。
所以,最好运用产品日志,详细地将反常记录到日志体系里。
反常处理时,功能开支大的当地
咱们从功能视点来审视一下 Java 的反常处理机制,这里有两个功能开支相对大的当地:
try-catch 代码段会产生额定的功能开支,或许换个视点说,它往往会影响 JVM 对代码进行优化,所以主张仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;
Java 每实例化一个 Exception,都会对其时的栈进行快照,这是一个相对比较重的操作。假如实例化 Exception 产生的非常频频,这个开支可就不能被忽略了。
当咱们的服务呈现反响变慢、吞吐量下降的时分,查看产生最频频的 Exception 也是一种思路。

赞(0)
未经允许不得转载:志达IT网站 » java的异常分为哪两类(java的异常处理机制)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

志达IT网站 每天分享编程和互联网的IT技术博客

登录/注册联系我们