Java 异常处理机制的原理
Java 8 在线类库:https://www.matools.com/api/java8
Java 内置异常类:https://www.runoob.com/java/java-exceptions.html
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
例如:代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error
例如:用 System.out.println(11/0),用0做了除数,会抛出异常 java.lang.ArithmeticException
异常发生的原因有很多,通常包含以下几大类:
1)用户输入了非法数据。
2)要打开的文件不存在,没有读写权限。
3)网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
Java异常处理是如何工作的,需要掌握以下三种类型的异常:
1)错误(Error)
错误不是异常,而是脱离程序员控制的问题,错误在代码中通常被忽略。
例如,JVM中堆内存溢出,栈溢出,死锁,它们在编译也检查不到的,程序是没有处理机会的。
2)运行时异常(RuntimeException)
运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
运行时异常,是指在Java编译器编译阶段会忽略,不会抛出和检查,但当在加载运行后,出现的异常;常见的运行时异常有:ArrayIndexOutofException、NumberFormatException、NullPointerException、ClassCastException、ClassNotFoundException等。
3)非运行时异常(检查性异常,例如 IOException、SQLException等)
非运行时异常,也叫可处理异常,程序编译时就提示的异常。
最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。
例如:要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不可预测,需程序处理。
还有一些FileNotFoundException、IOException、SQLException、自定义异常等。
4)自定义异常
自定义异常,是根据需求,使用throw关键字引发异常。
// 1. 自定义异常类继承Exception class TestException extends Exception { String message = ""; public TestException(String message) { this.message = message; } @Override public String toString() { return message; } } // 2. 自定义异常, 使用throw设置触发 public class Demo { public static void main(String[] args) throws Exception{ String e = "asdfabc"; if(e.contains("abc")){ throw new TestException("包含非法字符"); } } }
小结
Error 是我们处理不了的异常,我们要处理的异常有两种:
1)非运行时异常(编译时就会被检测到的异常),该异常在编译时,如果没有处理(没有抛也没有try),会编译失败。该异常会被eclipse标识,代表这可以被处理。
2)运行时异常(编译时不检测,运行时才出现的异常),该异常的发生是在JVM运行过程中才会出现,我们需要对这些代码进行修正,遇到时要进行处理。
Exception 类的层次
所有的异常类是从 java.lang.Exception 类继承的子类。
而Exception 类又是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error
Java 程序通常不捕获错误,错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。例如,JVM 内存溢出,一般地程序不会从错误中恢复。
异常类有两个主要的子类:IOException类 和 RuntimeException类
在 Java 内置类中(接下来会说明),有大部分常用检查性和非检查性异常。
Java 异常处理机制的原理
Java异常是程序在运行时(非编译)所发生的非正常情况或错误。
Java对异常进行了分类,不同类型的异常使用了不同的java类,
所有异常的根类为java.lang.Throwable.Throwable派生了2个子类:Error 和 Exception
Error表示程序本身无法克服和恢复的一种严重错误,程序只有死的份,如内存溢出和死锁问题等系统问题。
Exception表示还能克服和恢复,其中又分为系统异常 和 普通异常。
1、系统异常,是软件本身缺陷导致的问题,也就是软件开发问题考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但这种情况下可以选择让软件继续运行或死掉。如数组越界问题(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException),类转换异常(ClassCastException);
2、普通异常,是运行环境的变化或异常导致的问题,是用户能够克服的问题,如网路掉线、硬盘空间不足、IO异常发生这种异常后程序不应该死掉。
Java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或throws声明继续抛给上层调用方法处理,所以普通异常为checked异常,而系统异常可以处理也可以不处理。编译器不强制用try..catch或throws声明,所以系统异常成为uncheckde异常。
请写出最常见到的5个运行时异常(runtime exception):
NullPionterException
ArrayIndexOutOfBoundsException
StringIndexOutOfBoundsException
ClassCastException
NumberFormatException
Java 语言定义了一些异常类在 java.lang 标准包中。
标准运行时异常类的子类,是最常见的异常类。
由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
Exception Hierarchy:
Throwable class is the root class for every exception and it branches out to 2 main categories i.e.;
1)Exception
2)Error
java.lang.Throwable:
- Throwable is the root class for exception & it’s sub-type and error & it’s sub-types
- In other words, it is super class for exception & error
- java.lang.Throwable class extends java.lang.Object class (as shown in the above figure)
- It defines 2 sub classes i.e.; Exception and Error
java.lang.Exception:
- java.lang.Exception is super class for all types of Exception
- It extends java.lang.Throwable class
- Exception are due to programmatic logic
- And it is recoverable
- Exception are categorized into checked exception and unchecked exception
- Example: RuntimeException, SQLException, IOException, FileNotFoundException, ArithmeticException, NullPointerException
java.lang.Error:
- java.lang.Error is super class for all types of Error
- It extends java.lang.Throwable class
- Error are due to lack of system resources
- And it is non-recoverable
- All error fall into unchecked exception category, as it is raised due to lack of system resources at runtime
- It is out of programming scope as such type of error can’t predicted, may be well planned care can be taken to avoid these kind of Error
- Example: VirtualMachineError, AssertionError, ExceptionInInitializerError, StackOverflowError, OutOfMemoryError, LinkageError, InstantiationError
Note: above mentioned Exception and Error are again categorized into checked and unchecked exceptions
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException |
当应用程序试图在需要对象的地方使用 null 时,抛出该异常
|
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException |
此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
|
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException |
当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
|
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException |
当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
|
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
异常方法
Throwable 类的主要方法:
序号 | 方法及说明 |
---|---|
1 |
public String getMessage() 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
2 |
public Throwable getCause() 返回一个Throwable 对象代表异常原因。 |
3 |
public String toString() 使用getMessage()的结果返回类的串级名字。 |
4 |
public void printStackTrace() 打印toString()结果和栈层次到System.err,即错误输出流。 |
5 |
public StackTraceElement [] getStackTrace() 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
6 |
public Throwable fillInStackTrace() 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
捕获异常
使用 try 和 catch 关键字可以捕获异常。
try-catch 代码块放在异常可能发生的地方。
try-catch代码块中的代码称为保护代码,使用 try-catch 的语法如下:
try { // 可能出现的异常程序代码 } catch(ExceptionName e1) { // 处理异常代码 } finally { // 释放资源,一定会执行 }
catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
try - catch 使用的常用格式:
try...catch
try...catch...catch...
try...catch...catch...finally
异常处理原则,请理解并记住:
多个异常同时被捕获的时候,记住一个原则:先逮小的,再逮大的。
finally 永远被执行,除非退出JVM,即 System.exit(0)
finally的return会覆盖 try / catch 中的return
面试题:
1、try()里面有一个return语句,那么后面的finally{}里面的code会不会被执行,什么时候执行,是在return前还是return后?
写了个代码测试一下:
public static void main(String[] args) { int i = getInt(); System.out.println(i); } private static int getInt() { // TODO Auto-generated method stub try { return 0; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ return 1; } } // 显示输出结果为1,记住就行了,不想去钻这个问题的牛角尖,也没有什么大用处。
finally的return会覆盖catch中的return
2、error和exception有什么区别
error表示系统级的错误,是java运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是Java虚拟机抛出的。
exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序必须处理的问题
3、运行时异常和一般异常有何不同
Java提供了两类主要的异常:runtimeException 和 checkedException
1)一般异常(checkedException)主要是指IO异常、SQL异常等。对于这种异常,JVM要求我们必须对其进行catch处理,所以,面对这种异常,不管我们是否愿意,都是要写一大堆的catch块去处理可能出现的异常。
2)运行时异常(runtimeException)一般不处理,当出现这类异常的时候程序会由虚拟机接管。比如,我们从来没有去处理过NullPointerException,而且这个异常还是最常见的异常之一。
出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇到处理代码,如果没有catch块进行处理,到了最上层,如果是多线程就有Thread.run()抛出,如果不是多线程那么就由main.run()抛出。抛出之后,如果是线程,那么该线程也就终止了,如果是主程序,那么该程序也就终止了。
其实,运行时异常的也是继承自Exception,也可以用catch块对其处理,只是我们一般不处理罢了,也就是说,如果不对运行时异常进行catch处理,那么结果不是线程退出就是主程序终止。
如果不想终止,那么我们就必须捕获所有可能出现的运行时异常。如果程序中出现了异常数据,但是它不影响下面的程序执行,那么我们就该在catch块里面将异常数据舍弃,然后记录日志。如果,它影响到了下面的程序运行,那么还是程序退出比较好些。
4、Java中异常处理机制的原理
Java通过面向对象的方式对异常进行处理,Java把异常按照不同的类型进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它都是Throwable或其子类的实例。当一个方法出现异常后就会抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并对异常进行处理。
Java的异常处理是通过5个关键词来实现的:try catch throw throws finally
一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws),我们可以通过它的类型来捕捉它,或最后由缺省处理器来处理它(finally)。
try:用来指定一块预防所有异常的程序
catch:紧跟在try后面,用来捕获异常
throw:用来明确的抛出一个异常
throws:用来标明一个成员函数可能抛出的各种异常
finally:确保一段代码无论发生什么异常都会被执行的一段代码。
throw 和 throws 的区别
1)有throws的时候可以没有throw
有throw的时候,如果throw抛的异常是Exception体系,那么必须有throws在方法上声明。
2)用法区别
throws用于方法的声明上,其后跟的是异常类名,后面可以跟多个异常类,之间用逗号隔开
throw用于方法体中,其后跟的是一个异常对象名
5、你平时在项目中是怎样对异常进行处理的。
1)尽量避免出现runtimeException 。例如对于可能出现空指针的代码,在使用对象之前一定要判断一下该对象是否为空,必要的时候对runtimeException也进行try catch处理。
2)进行try catch处理的时候,要在catch代码块中对异常信息进行记录,通过调用异常类的相关方法获取到异常的相关信息,返回到web端,不仅要给用户良好的用户体验,也要能帮助程序员良好的定位异常出现的位置及原因。例如,以前做的一个项目,程序遇到异常页面会显示一个图片告诉用户,哪些操作导致程序出现了什么异常,同时图片上有一个按钮用来点击,展示异常的详细信息给程序员看的,定位异常,修复问题。
6、final、finally、finalize的区别
1)final 用于声明变量、方法和类的,分别表示变量值不可变,方法不可覆盖,类不可以继承
2)finally 是异常处理中的一个关键字,表示finally{}里面的代码一定要执行,即使前面return了也会执行
3)finalize 是Object类的一个方法,是方法函数,在垃圾回收的时候会调用被回收对象的此方法。
7、SpringMVC 异常处理的流程
全局异常处理器测试类,实现HandlerExceptionResolver接口
8、自定义异常类
自定义异常类,一般需继承Exception或者RuntimeException
1)如果希望写一个检查性异常类,则需要继承 Exception 类
2)如果你想写一个运行时异常类,那么需要继承 RuntimeException 类
为了让该自定义类具备可抛性,并具备操作异常的共性方法。
// 1. 继承异常类 Exception , 自定义检查性异常类 class MyExcepiton extends Exception { MyExcepiton(){} MyExcepiton(String message) { super(message); } } // 2. 继承运行时异常类 RuntimeException class MyException extends RuntimeException { MyExcepiton(){} MyExcepiton(String message) { super(message); } } // 测试自定义异常类 public class MyExceptionTest { public static void main(String[] args) { try{ int i = 1/0; }catch(Exception e){ throw new MyExcepiton("大件事了,出异常了!"); // 自定义运行时异常类 } } }
参考推荐:
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2021-04-05 05:19:15
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!
转载注明: Java 异常处理机制的原理 (米扑博客)