一、什么是异常?

异常就是有异于常态,和正常情况不一样,有错误出错。在java中,阻止当前方法或作用域正常运行的情况,称之为异常。

二、异常体系

Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。

Throwable分成了两个不同的分支:

Error:它表示不希望被程序捕获或者是程序无法处理的错误。Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。

Exception:它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

根据Javac对异常的处理要求,将异常类分为2类:

非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常,对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。常见的RuntimeException :

ArrayIndexOutOfBoundsException(数组下标越界)

NullPointerException(空指针异常)

ArithmeticException(算术异常)

MissingResourceException(丢失资源)

ClassNotFoundException(找不到类)

检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try catch finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等

如果只考虑Exception,则非检查异常(unckecked exception)等同于RuntimeException,检查异常等同于非运行时异常。

三、异常处理

异常的处理机制分为抛出异常和捕获异常

抛出异常:在当前环境下无法获得必要的信息来解决问题,你所能做的就是从当前环境中跳出,并把问题提交给上一级环境,这就是抛出异常时所发生的事情。抛出异常后,会有几件事随之发生。首先,是像创建普通的java对象一样将使用new在堆上创建一个异常对象;然后,当前的执行路径(已经无法继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方继续执行程序,这个恰当的地方就是异常处理程序或者异常处理器,它的任务是将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去。

捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws:

   try     -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。

  catch   -- 用于捕获异常。catch用来捕获try语句块中发生的异常。

  finally  -- finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。

  throw   -- 用于抛出异常。

throws -- 用在方法签名中,用于声明该方法可能抛出的异常。

捕获异常:try catch / try catch finally

1、try-catch语句

try {
//可能产生的异常的代码区,也成为监控区
}catch (ExceptionType1 e) {
//捕获并处理try抛出异常类型为ExceptionType1的异常
}catch(ExceptionType2 e) {
//捕获并处理try抛出异常类型为ExceptionType2的异常
}

2、try-catch-finally

try {
//可能产生的异常的代码区
}catch (ExceptionType1 e) {
//捕获并处理try抛出异常类型为ExceptionType1的异常
}catch (ExceptionType2 e){
//捕获并处理try抛出异常类型为ExceptionType2的异常
}finally{
//无论是出现异常,finally块中的代码都将被执行
}

3、try-catch-finally代码块的执行顺序:

A)try没有捕获异常时,try代码块中的语句依次被执行,跳过catch。如果存在finally则执行finally代码块,否则执行后续代码。

B)try捕获到异常时,如果没有与之匹配的catch子句,则该异常交给JVM处理。如果存在finally,则其中的代码仍然被执行,但是finally之后的代码不会被执行。

C)try捕获到异常时,如果存在与之匹配的catch,则跳到该catch代码块执行处理。如果存在finally则执行finally代码块,执行完finally代码块之后继续执行后续代码;否则直接执行后续代码。另外注意,try代码块出现异常之后的代码不会被执行。

注意点:

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

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

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

小结:

  1、不管有木有出现异常或者try和catch中有返回值return,finally块中代码都会执行;

  2、finally中最好不要包含return,否则程序会提前退出,返回会覆盖try或catch中保存的返回值。

  3.  e.printStackTrace()可以输出异常信息。

  4.  return值为-1为抛出异常的习惯写法。

  5.  如果方法中try,catch,finally中没有返回语句,则会调用这三个语句块之外的return结果。

6.  finally 在try中的return之后 在返回主调函数之前执行。

接下来看两段有意思的代码:

代码一:

public class TestFinally {
public static void main(String[] args){
int[] a = test1();
System.out.println(a[0]);
}
public static int[] test1(){
int[] a = new int[10];
a[0]=1;
try{
a[0]=2;
return a;
}catch(Exception e){
e.printStackTrace();
}finally{
a[0] =3;
System.out.println("执行了finally");
}
return a;
}
}

代码二:

public class TestFinally {
public static void main(String[] args){
System.out.println(test1());
}
public static int test1(){
int i= 1;
try{
i=2;
return i;
}catch(Exception e){
e.printStackTrace();
}finally{
i =3;
System.out.println("执行了finally");
}
return i;
}
}

为什么第一段代码中的finally覆盖了try中的return,而第二段代码就没有覆盖呢?

在debug的调试过程中,try中的return语句执行了两次,在try中第一次执行到return语句时,不会真正的return,即只是会计算return中的表达式,之后将结果保存在一个临时栈中,接着执行finally中的语句,最后才会从临时栈中取出之前的结果返回。所以在第一段代码中,保存的数组的地址,我们在finally中我们对其的内容修改,其实还是返回保存在临时栈中的地址,只是地址指向堆中的值改变了,而在第二段代码中,把i=2存在临时栈中,当我们执行完finally的时候,直接把保存的值i=2直接返回了。

有了这些认识之后,我们讨论一下try,catch,finally中有return语句的几种情况。

第一种:try{}catch(){}finally{}return;

该情况语句后顺序执行。(不考虑异常)

第二种:try{return;}catch(){}finally{}return;

该情况为刚才说的题目情况,即执行完try语句块,将return的值保存在临时栈中,再执行finally语句块,之后返回临时栈中的值。

第三种:try{}catch(){return;}finally{}return;

无异常:执行try,执行finally,再执行return;

有异常:执行完catch语句块,将return的值保存在临时栈中,再执行finally语句块,之后返回临时栈中的值。

第四种:try{}catch(){}finally{return;}

执行finally中的return语句。

第五种:try{return;}catch(){return;}finally{};

根据有无异常执行和情况二或情况三。

第六种:try{return;}catch(){}finally{return;}

执行完try语句块,将return的值保存在临时栈中,再执行finally语句块,因为finally中有return,所以返回finally中的return值。

第七种:try{}catch(){return;}finally{return;}

执行完catch语句块,将return的值保存在临时栈中,再执行finally语句块,因为finally中有return,所以返回finally中的return值。

第八种:try{return;}catch(){return;}finally{return;}

有异常:执行情况七。

无异常:执行情况六。

抛出异常:throw/throws

throws 函数声明

声明将要抛出何种类型的异常(声明)。

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

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

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

public void 方法名(参数列表)
throws 异常列表{
//调用会抛出异常的方法或者:
throw new Exception();
}

throw ----将产生的异常抛出,是抛出异常的一个动作

一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。

程序执行完throw语句之后立即停止;throw后面的任何语句不被执行,

public static void main(String[] args) {
String s = "abc";
if(s.equals("abc")) {
throw new NumberFormatException();
} else {
System.out.println(s);
}
//function();
}

throw与throws的比较
1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

使用throw和throws关键字需要注意以下几点:

1.throws的异常列表可以是抛出一条异常,也可以是抛出多条异常,每个类型的异常中间用逗号隔开

2.方法体中调用会抛出异常的方法或者是先抛出一个异常:用throw new Exception() throw写在方法体里,表示“抛出异常”这个动作。

3.如果某个方法调用了抛出异常的方法,那么必须添加try catch语句去尝试捕获这种异常, 或者添加声明,将异常抛出给更上一层的调用者进行处理

四、自定义异常

使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。

在程序中使用自定义异常类,大体可分为以下几个步骤:

1、创建自定义异常类。

2、在方法中通过throw关键字抛出异常对象。

3、如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。

4、在出现异常方法的调用者中捕获并处理异常。

举例自定义异常:

class MyException extends Exception {
private int detail;
MyException(int a){
detail = a;
}
public String toString(){
return "MyException ["+ detail + "]";
}
}
public class TestMyException{
static void compute(int a) throws MyException{
System.out.println("Called compute(" + a + ")");
if(a > 10){
throw new MyException(a);
}
System.out.println("Normal exit!");
}
public static void main(String [] args){
try{
compute(1);
compute(20);
}catch(MyException me){
System.out.println("Caught " + me);
}
}
}

参考:

https://blog.csdn.net/qq_30816657/article/details/80297646

https://blog.csdn.net/zhanaolu4821/article/details/81012382

https://www.cnblogs.com/hysum/p/7112011.html

https://blog.csdn.net/yongyuai/article/details/79752608

https://blog.csdn.net/ShyTan/article/details/81434219

java之异常详解的更多相关文章

  1. JAVA基础——异常详解

    JAVA异常与异常处理详解 一.异常简介 什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错.在java中,阻止当前方法或作用域的情况,称之为异常. java中异常的体系是怎么样的呢? 1 ...

  2. Java基础 - 异常详解

    异常的层次结构 Throwable Throwable 是 Java 语言中所有错误与异常的超类. Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示 ...

  3. java笔记--异常详解与处理

    一.异常概念 Throwable类是Java中所有错误或异常的超类. 1.只有当对象是此类(或其子类)的实例时,才能通过Java虚拟机或着Java throw语句抛出.     2.只有此类或其子类才 ...

  4. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  5. 转:Java HashMap实现详解

    Java HashMap实现详解 转:http://beyond99.blog.51cto.com/1469451/429789 1.    HashMap概述:    HashMap是基于哈希表的M ...

  6. 淘宝JAVA中间件Diamond详解(2)-原理介绍

    淘宝JAVA中间件Diamond详解(二)---原理介绍 大家好,通过第一篇的快速使用,大家已经对diamond有了一个基本的了解.本次为大家带来的是diamond核心原理的介绍,主要包括server ...

  7. Java集合类的详解与应用

    Java集合类的详解与应用 集合简介: 1.定义:可以同时存储不同类型的数据 他的存储空间会随着数据的增大而增大 2.缺点:只能存储引用数据类型 3.优点:更加合理的利用空间,封装了更多的方法,用起来 ...

  8. Java 反射机制详解(下)

    续:Java 反射机制详解(上) 三.怎么使用反射 想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属 ...

  9. java异常处理机制详解

    java异常处理机制详解 程序很难做到完美,不免有各种各样的异常.比如程序本身有bug,比如程序打印时打印机没有纸了,比如内存不足.为了解决这些异常,我们需要知道异常发生的原因.对于一些常见的异常,我 ...

随机推荐

  1. mimalloc剖析

    mimalloc是微软最近开源的一个malloc实现,其实验数据表明相比于jemalloc.tcmalloc等实现大约快了10%.其通过将空闲块列表(Free List)进行分片(Sharding)来 ...

  2. mybatis基础配置

    我这个写的比较简略,是自己短时间记录的可能只适合自己看,新手或者不懂的建议看看下面大神这篇: https://www.cnblogs.com/homejim/p/9613205.html 1.MyBa ...

  3. [转载] 管Q某犇借的手写堆

    跟gxy大神还有yzh大神学了学手写的堆,应该比stl的优先队列快很多. 其实就是维护了一个二叉堆,写进结构体里,就没啥了... 据说达哥去年NOIP靠这个暴力多骗了分 合并果子... templat ...

  4. 9.22考试 crf的军训 题解

    做这道题时由于第一道题太水了,第一反应是NOIP T2级别的题,需要拿上70~100的分,然后就开始分析,当然最后事实证明我错了…… 这道题当时首先联想到了 NOIP2016愤怒的小鸟 当然,数据范围 ...

  5. 黑羽压测 比 jmeter、locust、loadrunner 更简便,性能更强

    视频讲解 点击下方链接,观看 讲解视频 https://www.bilibili.com/video/av60089015/ 动机 目前市场上对API接口做性能测试工具有 Jmeter.LoadRun ...

  6. deque双端队列笔记

    clear()clear()clear():清空队列 pushpushpush_back()back()back():从尾部插入一个元素. pushpushpush_front()front()fro ...

  7. MYSQL数据库数据类型

    07.14自我总结 MYSQL数据库数据类型 一.整数类型和浮点数典型 1.有符号/没符号 对于整数和负整数来说,默认情况下是有符号范围的 默认是有符号 有符号和没符号其实就是有没有包括负数,有符号是 ...

  8. Spring Boot从入门到实战(十):异步处理

    原文地址:http://blog.jboost.cn/2019/07/22/springboot-async.html 在业务开发中,有时候会遇到一些非核心的附加功能,比如短信或微信模板消息通知,或者 ...

  9. [HDOJ] 1172.猜数字

    Problem Description 猜数字游戏是gameboy最喜欢的游戏之一.游戏的规则是这样的:计算机随机产生一个四位数,然后玩家猜这个四位数是什么.每 猜一个数,计算机都会告诉玩家猜对几个数 ...

  10. ssh,公钥和私钥,远程复制

    ssh,公钥和私钥,远程复制 1.openssh 是一个提供远程访问控制的软件 2.ssh 远程主机用户@远程主机ip地址 ssh root@172.25.254.1 The authenticity ...