Java异常体系和异常处理机制
异常简介
在程序运行过程中出现错误,导致程序出现非预期场景。异常处理可以保证出现错误后,控制接下来的程序流程,是选择定位错误信息,还是抛出异常或捕获异常、还是避免程序非正常退出,都取决于我们。
Java的异常体系结构(来自网络)
Java把异常作为一种类,当做对象来处理。所有异常类的基类是Throwable类,两大子类分别是Error和Exception。这些异常类可以分为三种类型:系统错误、异常和运行时异常。系统错误由Java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误,例如Java虚拟机崩溃。这种情况仅凭程序自身是无法处理的,在程序中也不会对Error异常进行捕捉和抛出。
异常(Exception)又分为RuntimeException(运行时异常)和CheckedException(检查异常),两者区别如下:
- RuntimeException:程序运行过程中出现错误,才会被检查的异常。例如:类型错误转换,数组下标访问越界,空指针异常、找不到指定类等等。
- CheckedException:来自于Exception且非运行时异常都是检查异常,编译器会强制检查并通过try-catch块来对其捕获,或者在方法头声明该异常,交给调用者处理。
两种异常的处理方式:若是运行时异常,则表明程序出错,应该找到错误并修改,而不是对其捕获。若是检查异常,遵循该原则:谁知情谁处理,谁负责谁处理,谁导致谁处理。处理就是对其捕获并处理。
深入了解异常处理
异常处理的5个关键字:try、catch、throw、throws和finally。关于它们的用法和注意点,会在下面一一介绍。
Java的异常处理模型基于三种操作: 声明异常、抛出一个异常和捕获一个异常。
声明异常(throws)
//不捕获,而是声明该异常,交给调用者处理
public static void method() { public static void method2() throws Exception {
/*try-catch块捕获异常*/ if(5 > 3) {
if(5 > 3) { throw new Exception(); //抛出异常
try { }
throw new Exception(); //抛出异常 }
} catch (Exception e) {
e.printStackTrace();//捕获异常后的处理
}
}
}
在Java中,当前执行的语句必属于某个方法,Java解释器调用main方法执行开始执行程序。若方法中存在检查异常,如果不对其捕获,那必须在方法头中显式声明该异常,以便于告知方法调用者此方法有异常,需要进行处理。 在方法中声明一个异常,方法头中使用关键字throws,后面接上要声明的异常。若声明多个异常,则使用逗号分割。如下所示:
public static void method() throws IOException, FileNotFoundException{
//something statements
}
【注意】若是父类的方法没有声明异常,则子类继承方法后,也不能声明异常。
抛出异常(throw)
如果代码可能会引发某种错误,可以创建一个合适的异常类实例并抛出它,这就是抛出异常。如下所示:
public static double method(int value) {
if(value == 0) {
throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常
}
return 5.0 / value;
}
大部分情况下都不需要手动抛出异常,因为Java的大部分方法要么已经处理异常,要么已声明异常。所以一般都是捕获异常或者再往上抛。
捕获异常(try-catch)
当抛出一个异常时,可以在try-catch块中捕获它并进行处理。
try {
//包含可能会出现异常的代码以及声明异常的方法
} catch (ClassCastException e) {
//捕获指定异常并进行处理
}catch(Exception ex) {
//捕获指定异常并进行处理
}
若执行try块的过程中没有发生异常,则跳过catch子句。若是出现异常,try块中剩余语句不再执行。开始逐步检查catch块,判断catch块的异常类实例是否是捕获的异常类型。匹配后执行相应的catch块中的代码。如果异常没有在当前的方法中被捕获,就会被传递给该方法的调用者。这个过程一直重复,直到异常被捕获或被传给main方法(交给JVM来捕获)。
catch捕获异常的顺序
一个通用父类可以派生出各种异常类,如果一个catch块可以捕获一个父类的异常对象,它就能捕获那个父类的所有子类的异常对象。如果捕获的是多个同类型异常,则子类异常在前,父类异常在后,不然会导致编译错误。这是因为父类异常囊括了子类异常,如果父类异常在前,子类异常永远捕获不到,导致有时候无法准确描述错误信息。
try {
File f =new File("C:\\ProgramFile\\test.txt");
FileInputStream fis = new FileInputStream(f);
} catch (FileNotFoundException e) { //子类异常
e.printStackTrace();
} catch(IOException ie) { //父类异常
ie.printStackTrace();
} catch(Exception e) { //基类运行时异常
e.printStackTrace();
}
这里只是用于演示catch块捕获的顺序。捕获多个异常时,可以使用catch(Exception1 | Exception2| Exception3)的形式来优化捕获异常的代码结构。
将声明异常、抛出异常和捕获异常综合在一起。演示如下:
public static void main(String[] args) {
for(int i = 2; i < 100; i++) {
//对运行时异常,可以选择捕获也可以不选择捕获
if(isPrime(i)) {
System.out.print(i + " ");
}
}
} //检测是否为质数
public static boolean isPrime(int num) throws IllegalArgumentException{
//抛出一个运行时异常
if(num < 2) throw new IllegalArgumentException("质数不能小于2");
for(int i = 2; i < num; i++) {
if(num % i == 0) {//若能被1和本身之外的数整除,则非质数
return false;
}
}
return true;
}
因为抛出的是运行时异常,可以选择捕获或者不捕获。但如果抛出检查异常,在编译时就必须选择捕获或者声明。
finally语句块
无论是否有异常,finally块中的代码总是会被执行的。 finally语句在执行关闭资源的语句时非常有用。
//第一种形式 //第二种形式
try { try {
//执行程序代码,可能会出现异常 //执行程序代码,可能会出现异常
}catch(Exception e) { }finally {
//捕获异常并处理 //必执行的代码
}finally { }
//必执行的代码
}
try-catch-finally的执行流程
try块中引起异常,异常代码之后的语句不再执行,若被catch块捕获,执行匹配的catch块,然后执行finally语句。若catch块不能捕获异常,则执行finally语句,之后将异常传递给这个方法的调用者。
Scanner sc = new Scanner(System.in);
int a = 0; //保证局部变量a在各个块中可用
try {
a = sc.nextInt();
if(a < 0) throw new IllegalArgumentException();
System.out.println("执行完try语句。a:" + a);
}catch(IllegalArgumentException e){
System.out.println("执行catch语句");
System.out.println("数值小于0,不符合。已设为默认值 1");
a = 1;
}finally {
System.out.println("执行finally语句。a:" + a);
}
//未引发异常 //引发异常并捕获
5 -5
执行完try语句。a:5 执行catch语句
执行finally语句。a:5 数值小于0,不符合。已设为默认值 1
执行finally语句。a:1
try-finally的执行流程
try块中引起异常,异常代码之后的语句不再执行,直接执行finally语句。 try块没有引发异常,则执行完try块就执行finally语句。
try-finally可用在不需要捕获异常的代码,可以保证资源在使用后被关闭。例如IO流中执行完相应操作后,关闭相应资源;使用Lock对象保证线程同步,通过finally可以保证锁会被释放;数据库连接代码时,关闭连接操作等等。
//以Lock加锁为例,演示try-finally
ReentrantLock lock = new ReentrantLock();
try {
//需要加锁的代码
}finally {
lock.unlock(); //保证锁一定被释放
}
- 在前面的代码中用了System.exit()退出程序。
- finally语句块中发生了异常。
- 程序所在的线程死亡。
- 关闭CPU。
try、catch、finally、throw和throws使用归纳
- try、catch和finally都不能单独使用,只能是try-catch、try-finally或者try-catch-finally。
- try语句块监控代码,出现异常就停止执行下面的代码,然后将异常移交给catch语句块来处理。
- finally语句块中的代码一定会被执行,常用于回收资源 。
- throws:声明一个异常,告知方法调用者。
- throw :抛出一个异常,至于该异常被捕获还是继续抛出都与它无关。
- 在恰当的级别处理问题。(在知道该如何处理的情况下了捕获异常。)
- 解决问题并且重新调用产生异常的方法。
- 进行少许修补,然后绕过异常发生的地方继续执行。
- 用别的数据进行计算,以代替方法预计会返回的值。
- 把当前运行环境下能做的事尽量做完,然后把相同的异常重抛到更高层。
- 把当前运行环境下能做的事尽量做完,然后把不同的异常抛到更高层。
- 终止程序。
- 进行简化(如果你的异常模式使问题变得太复杂,那么用起来会非常痛苦)。
- 让类库和程序更安全。
自定义异常
通过继承Exception类来定义一个异常类。Java已经提供了很多异常类,尽量使用这些异常类而不要创建自己的异常类。除非Java的异常类不能很好地描述问题时,才自定义异常来进行准确描述。对于自定义异常,不需要太多功能,类名能准确描述问题是关键。
自定义异常如下所示:
//判断长度是否合法的自定义异常
public class LengthException extends Exception {
public LengthException() {}
public LengthException(String s) {
super(s);
}
@Override
public String getMessage() {
return super.getMessage();
}
}
Java异常体系和异常处理机制的更多相关文章
- Java 异常体系(美团面试)
Java把异常作为一种类,当做对象来处理.所有异常类的基类是Throwable类,两大子类分别是Error和Exception. 系统错误由Java虚拟机抛出,用Error类表示.Error类描述的是 ...
- java(异常体系及权限修饰符)
java异常体系 异常的体系: 异常体系: --------| Throwable 所有错误或者异常的父类 --------------| Error(错误) --------------| Exce ...
- Java异常体系概述
Java的异常体系结构 Java异常体系的根类是 Throwable, 所以当写在java代码中写throw抛出异常时,后面跟的对象必然是Throwable或其子类的对象. 其中Exception异常 ...
- Java异常处理-----java异常体系
再三思考后还是决定贴图,csdn的格式,我是真玩不转,对不起了各位,继续将就吧. 错误原因:内存溢出.需要的内存已经超出了java虚拟机管理的内存范围. 错误原因:找不到类文件. 错误(Error): ...
- Java基础系列5:深入理解Java异常体系
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 前言: Java的基 ...
- Java——深入理解Java异常体系
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 前言: Java的基 ...
- Java & Android未捕获异常处理机制
一.背景 无论是Java还是Android项目,往往都会用到多线程.不管是主线程还是子线程,在运行过程中,都有可能出现未捕获异常.未捕获异常中含有详细的异常信息堆栈,可以很方便的去帮助我们排查问题. ...
- java 异常体系详细介绍
一.异常概述与异常体系结构 异常:在Java语言中,将程序执行中发生的不正常情况称为"异常".(开发过程中的语法错误和逻辑错误不是异常). Java把异常当作对象来处理,并定义一个 ...
- JAVA异常体系
1.异常体系 ----|Throwable 所有错误或异常的父类 --------|Error(错误) --------|Exception(异常)一般能通过代码处理 ------------|运行时 ...
随机推荐
- 微信小程序-view组件
<view class="section"> <view class="section__title">flex-direction: ...
- 新手嘛,先学习下 Vue2.0 新手入门 — 从环境搭建到发布
Vue2.0 新手入门 — 从环境搭建到发布 转自:http://www.runoob.com/w3cnote/vue2-start-coding.html 具体文章详细就不搬了,步骤可过去看,我这就 ...
- 【html/css】模态框的实现
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- 线性表接口的实现_Java
线性表是其组成元素间具有线性关系的一种线性结构,对线性表的基本操作主要有插入.删除.查找.替换等,这些操作可以在线性表的任何位置进行.线性表可以采用顺序存储结构和链式存储结构表示. 本接口的类属于da ...
- C# 递归函数详细介绍及使用方法
什么是递归函数/方法? 任何一个方法既可以调用其他方法也可以调用自己,而当这个方法调用自己时,我们就叫它递归函数或递归方法. 通常递归有两个特点: 1. 递归方法一直会调用自己直到某些条件被满足 2. ...
- 二、Asp.Net Core WebAPI——OcelotDemo
项目源码OcelotDemo 基础知识在教程或者官网文档查看 Ocelot源码 基于.NET平台的Ocelot网关框架教程汇总 这篇文章不错. 这里我只写我想说的 项目结构 API1和API2是测试的 ...
- 执行系统命令,subprocess使用说明
os.system('ls -l') #只执行命令,不能将结果赋予变量 os.system('mkdir test') #创建test目录 files = os.popen('ls -l').rea ...
- Python学习---JSONP学习180130
同源策略机制 同源:协议://IP:端口[协议,域名,端口相同] 跨域:知道对方接口,同时对方返回的数据也必须是Jsonp格式的 问题描述:Ajax跨域请求数据的时候,实际浏览器已 ...
- 最优化作业 共轭梯度法 matlab代码
syms f x1 x2 f=(1/2)*x1^2+x2^2; x=[2;1]; a=[1 0;0 2];% A g1=diff(f,x1); g2=diff(f,x2); g=[g1;g2];%导数 ...
- Asp.Net MVC Identity 2.2.1 使用技巧(八)
一.添加管理链接 在View/Shared/_layout.cshtml,在页面导航上(28行)添加如下代码: @*通过身份验证并确认用户属于Admin角色显示管理菜单*@ @if (Request. ...