菜鸡的Java笔记 第三十 - java 异常的捕获及处理

时间:2023-01-14 21:54:16

异常的捕获及处理
        1.异常的产生分析以及所带来的影响
        2.异常的处理的基本格式
        3.异常的处理流程
        4.异常的处理模式
        5.自定义异常
        
        java中最为优秀的设计就在于异常的处理上,而且很简单
    
    认识异常

  异常都是从Throwable类派生出来的,而Throwable类是直接从Object类继承而来。你可以在Java SE官方API文档中获取更多关于它们的知识。

    异常通常有四类:
      Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理
      Exception:可以处理的异常
      RuntimeException:可以捕获,也可以不捕获的异常
      继承Exception的其他类:必须捕获,通常在API文档中会说明这些方法抛出哪些异常

    算术异常 (ArithmeticException)
      当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
      由于在Java中,浮点数(无论是float还是double类型的浮点数)被0除,并不会引发算术异常。

    数组下标越界异常 (ArrayIndexOutOfBoundsException)
      用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
      通俗的来说,例如你的数组大小是6,你能访问的范围即0到5,如果你用下标13去访问其中的元素,就会遇到此问题。

    空指针异常(NullPointerException)
      当应用程序试图在需要对象的地方使用 null 时,抛出该异常。这种情况包括:
        调用 null 对象的实例方法。
        访问或修改 null 对象的字段。
        将 null 作为一个数组,获得其长度。
        将 null 作为一个数组,访问或修改其时间片。
        将 null 作为 Throwable 值抛出。
      应用程序应该抛出该类的实例,指示其他对 null 对象的非法使用。

异常指的是导致程序中端执行的一种指令流。一旦产生异常并且没有正常处理的话,那么程序将会中断执行
        范例:观察没有产生异常的代码

    public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
System.out.println("**************"+(10 + 2)+"***************");
System.out.println("*****************************");
}
}

此时程序没有任何的问题,于是正常执行完毕

    public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
System.out.println("**************"+(10 / 0)+"***************");
System.out.println("*****************************");
}
}// 结果:异常

此时的程序产生了异常,所以最终发现程序并没有正常执行完毕,而是打印了一行信息之后直接就进行了退出程序
        
    处理异常
        如果要在java中进行异常的处理可以使用三个关键字的组合完成:try , catch , finally 。
        对于这三个关键字的组合可以有如下的使用语法
            try{
                // 有可能出现异常的语句
            }[catch (异常类型对象){
                异常处理
            }catch (异常类型对象){
                异常处理
            }......][finally{
                 // 异常的统一出口代码
            }]
            
        而此时给出的语法也有三种组合模式: try...catch,try...catch..finally,try...finally
            
        范例:实现异常的处理操作

public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
try{
int result = 10/0;
System.out.println("**************"+(10 / 0)+"***************");
}catch(ArithmeticException e){ // e 是一个对象
System.out.println(e);
}
System.out.println("*****************************");
}
}

此时的程序出现了异常之后,那么可以进行正常的执行完毕。并没有出现程序退出。不过这个代码里面也会发现点问题
        如果要想输出异常信息比较完整一些,则可以使用 printStackTrace();

public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
try{
int result = 10/0;
System.out.println("**************"+(10 / 0)+"***************");
}catch(ArithmeticException e){ // e 是一个对象
e.printStackTrace();
}
System.out.println("*****************************");
}
}

可以发现,此时异常出现的信息是非常完整的,会明确的告诉用户到底是哪行代码出现了问题
        除了使用 try...catch 的结构之外也可以利用 try...catch..finally 结构进行异常的处理
        
        范例:使用 try...catch..finally 类处理异常

public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
try{
int result = 10/0;
System.out.println("**************"+(10 / 0)+"***************");
}catch(ArithmeticException e){ // e 是一个对象
e.printStackTrace();
}finally{
System.out.println("ssssssssssssssssss");
}
System.out.println("*****************************");
}
}

异常产生之后找到了相应的 catch 语句执行,而后处理异常完毕后继续执行 finally 的代码

    多个异常的处理
        在一个 try 语句之后可以编写多个 catch 进行处理。
        模拟一个输入的数字计算操作,假设现在要计算的两个数字通过初始化参数设置上的
        范例:修改程序代码

public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
try{
int x = lnteger.parselnt(aegs[0]);
int y = lnteger.parselnt(aegs[1]);
int result = x/y; // 进行计算
System.out.println("**************"+(10 / 0)+"***************");
}catch(ArithmeticException e){ // e 是一个对象
e.printStackTrace();
}finally{
System.out.println("ssssssssssssssssss");
}
System.out.println("*****************************");
}
}

于是下面就有了这样几种执行情况:
            执行程序的时候没有设置初始化参数 : 数组越界异常
            执行程序的时候输入的内容不是数字(java Abnormal a b);NumberFormatException
            执行的时候输入的被除数为 0(java Abnormal 10 0):ArithmeticException
        如果现在异常已经正常进行处理了,则最后的语句一定会执行,而如果没有处理,最后的语句将不执行,但是 finally 的代码永远都会出现
        为了保证程序出现错误之后依然可以正常的执行完毕,那么可以采用多个 catch 处理
        
        范例:修改代码,处理多个异常

public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
try{
int x = lnteger.parselnt(aegs[0]);
int y = lnteger.parselnt(aegs[1]);
int result = x/y; // 进行计算
System.out.println("**************"+(10 / 0)+"***************");
}catch(ArithmeticException e){ // e 是一个对象
e.printStackTrace();
}catch(ArraylndexOutOfBoundsException e){ // e 是一个对象
e.printStackTrace();
}catch(NumberFormatException e){ // e 是一个对象
e.printStackTrace();
}finally{
System.out.println("ssssssssssssssssss");
}
System.out.println("*****************************");
}
}

这种代码使用if判断也行啊,折腾什么异常?
        提示:如果在 try 语句之中真的需要进行多个异常处理,那么捕获范围大的异常要放在捕获范围小的异常之后,否则会出现语法错误
        
    异常处理流程
        对于整个程序的异常处理,以上只是针对于语法进行了基本的使用说明,但是从本质上来讲,java中的异常处理要比之前讲解的过程容易
        为了分析这个流程,下面先来看两个异常类的继承结构

菜鸡的Java笔记 第三十 - java 异常的捕获及处理
            
        可以发现两个异常类都是 java.lang.Throwable 的子类,而现在来观察 Throwable 类
            此类定义如下:
                public class Throwable extends Object implements Serializable
                
            可以发现 Throwable 直接是 Object 子类。从JDK 1.0开始提供了
            但是在 Throwable 下有两个子类,因此在开发中几乎不会考虑 Throwable 处理
            
        面试题:请解释 Throwable 下的 Error 和Exception 子类的区别    
            Error:是在程序还未执行时出现的错误,一般指的都是JVM 出错,用户无法处理
            Exception:指的是在程序运行之中出现的异常,异常处理都是针对于此类型完成的
        因此在以后的开发中,如果处理异常,能够处理的最大的父类就是 Exception
        
        那么下面就可以根据以上给出的结论类分析异常处理流程(面试题)
            1.当程序中产生异常之后,JVM 自动的根据异常类型实例化一个指定的异常类对象
            2.程序需要判断当前的代码之中是否存在异常处理逻辑,如果没有,则交由JVM默认处理,处理方式输出异常信息,而后中断程序执行
            3.如果程序存在有异常处理,则 try 语句会捕获该异常类的实例化对象(想象为引用传递)
            4.捕获到的异常类的实例化对象要与 catch 中的异常类型进行依次匹配
            5.如果 catch 匹配了该异常类型则使用相应代码进行处理,随后要执行 finally 代码,如果没有任何一个 catch匹配,则直接跳转到 finally 语句
            6.执行完 finally 代码之后要判断该异常是否已经处理过了,如果已经处理过了,则继续执行后续代码,而如果没有处理,则程序将继续交由 JVM 默认处理
            
            通过以上的分析,可以发现所谓的 catch 匹配异常,就跟方法的参数传递没有什么区别,有一个实例化对象了,如果发现类型匹配,则进行接收
            那么按照对象向上转型的原则,所有的子类对象都可以向父类转换,那么也就证明所以的异常实际上都可以使用 Exception 进行处理
        
        范例:直接使用 Exception 处理异常

public class Abnormal{
public static void main(String aegs){
System.out.println("*****************************");
try{
int x = lnteger.parselnt(aegs[0]);
int y = lnteger.parselnt(aegs[1]);
int result = x/y; // 进行计算
System.out.println("**************"+(10 / 0)+"***************");
}catch(Exception e){ // e 是一个对象
e.printStackTrace();
}finally{
System.out.println("ssssssssssssssssss");
}
System.out.println("*****************************");
}
}

在实际的开发过程之中,如果你的开发团队有明确的要求,那么就分开处理
        但是如果要求不明确的情况下,使用 Exception 处理是最方便的

    throws 关键字
        异常的处理语句本身使用并不复杂,但是最为麻烦的是,执行到某段代码的时候,用户如何知道这个代码会产生什么异常?
        所以在java中提供有一个 throws 关键字,这个关键字的主要目的是明确的告诉用户执行某一个方法中有可能会产生哪些异常
        所以 throws 主要是用于方法的声明处
        范例:观察 throws 关键字的使用

    class MyMath{
public static int diy(int x,int y)throws Exception{
return x/y;
}
}
public class Abnormal{
public static void main(String aegs){
}
}

在这个 diy () 方法上那么就表示,如果要想执行本语句,那么就必须进行异常的处理
        范例:不处理异常,而直接调用

    class MyMath{
public static int diy(int x,int y)throws Exception{
return x/y;
}
}
public class Abnormal{
public static void main(String aegs){
System.out.println(MyMath.diy(10,2));
}
}// 结果出错

如果程序中使用了 throws 的声明,那么就表示必须强制性进行异常的处理操作
        范例:正确的调用形式

    class MyMath{
public static int diy(int x,int y)throws Exception{
return x/y;
}
}
public class Abnormal{
public static void main(String aegs){
try{
System.out.println(MyMath.diy(10,2));
}catch(Exception e){
e.printStackTrace();
}
}
}

但是使用了 throws之后还存在有一个传递问题,主方法也是一个方法
            所以主方法上也可以使用 throws ,那么就表示此时的主方法不进行异常的处理,而交给调用处处理

    class MyMath{
public static int diy(int x,int y)throws Exception{
return x/y;
}
}
public class Abnormal{
public static void main(String aegs)throws Exception{
System.out.println(MyMath.diy(10,2));
}
}

主方法之上那么就由 JVM 进行默认的处理,所以一般而言,如果你直接写 java 代码,主方法中应该把异常全部处理完成
            
    throw 关键字的使用    
        现在为止所有的异常对象都是由java负责进行实例化的,我们能够进行的处理只能够进行捕获
        但是在java 中允许用户自己去实例化异常类对象,那么如果要抛出这个对象,就必须使用 throw 关键字
        范例:自己手工抛出异常

    public class Abnormal{
public static void main(String aegs){//throws Exception{
throw new Exception("*****");
}
}

只要出现了 throw ,那么就表示代码中产生了异常,此时就两个选择:
                方法上使用 throws 继续抛出
                手工使用异常处理
                
        范例:异常处理

    public class Abnormal{
public static void main(String aegs)throws Exception{
try{
throw new Exception("*****");
}catch(Exception e){
e.printStackTrace();
}
}
}

        面试题:请解释 throw 与 throws 的区别
            throw 在方法体中使用,表示手工抛出一个异常类的实例化对象
            throws 在方法的声明中使用,表示此方法调用时必须明确进行异常的处理
            
    异常处理的实际应用
        对于异常的处理已经学习过了: try,catch,finally,throw,throws, 其中感觉 finally,throw 存在的意义不大
        但是实际上这些关键字如果真要使用,肯定一起使用
        现在假设要定义一个实现除法计算的方法,但是此方法的设计有如下要求:
            在进行除法计算开始要求可以打印提示信息,例如:“***计算开始***”;
            在进行除法计算完成之后要求可以打印提示信息,例如:“***计算结束***”;
            如果除法操作中出现了问题,那么必须交给调用处处理

class MyMath{
public static int diy(int x,int y)throws Exception{ // 交给被调用处处理
int result = 0; // 保存计算结果
System.out.println("*****除法计算开始*****");
return x/y;
System.out.println("*****除法计算结束*****");
return result;
}
}
public class Abnormal{
public static void main(String aegs){
try{
System.out.println(MyMath.diy(10,2));
}catch(Exception e){
e.printStackTrace();
}
}
}

一程序出现了错误,这个时候的执行结果.......
        范例:加入异常控制

class MyMath{
public static int diy(int x,int y)throws Exception{ // 交给被调用处处理
int result = 0; // 保存计算结果
System.out.println("*****除法计算开始*****");
try{
return x/y;
}catch(Exception e){
throw e; // 继续向上抛
}finally{
System.out.println("*****除法计算结束*****");
}
return result;
}
}
public class Abnormal{
public static void main(String aegs){
try{
System.out.println(MyMath.diy(10,2));
}catch(Exception e){
e.printStackTrace();
}
}
}

在以后的开发之中,一定会牵扯到资源的使用,例如:文件,数据库,数据库操作前一定要打开,操作后一定要关闭
        但是以上给出的异常处理模型可以简化: try...finally

class MyMath{
public static int diy(int x,int y)throws Exception{ // 交给被调用处处理
int result = 0; // 保存计算结果
System.out.println("*****除法计算开始*****");
try{
return x/y;
}finally{
System.out.println("*****除法计算结束*****");
}
return result;
}
}
public class Abnormal{
public static void main(String aegs){
try{
System.out.println(MyMath.diy(10,2));
}catch(Exception e){
e.printStackTrace();
}
}
}

RuntimeException
        首先下段代码

            public class Abnormal{
public static void main(String aegs){
int num = Integer.parseInt("123");
System.ou.println(num*num);
}
}

于是打开 Integer 类中的 parseInt() 方法定义来看
                public static int parseInt(String s) throws NumberFormatException
            从理论上来讲,方法中出现有 throws ,那么就需要进行异常处理,但是此时并没有处理,观察 NumderFormatExceptio 类的继承结构
                java.lang.NumberFormatException
                    java.lang.Object
                        java.lang.Throwable
                            java.lang.Exception
                                java.lang.RuntimeException
                                    java.lang.IllegalArgumentException
                                        java.lang.NumberFormatException
                                        
            因为 NumberFormatException 属于 RuntimeException 所以给异常属于选择性处理,用户即使不处理在程序编译的时候也不会发生错误,但是执行的时候会出错
        面试题:请解释 Exception 与 RuntimeException 的区别?列举出几个你常见的 RuntimeException
            RuntimeException 是 Exception 的子类
            Exception 定义的异常都需要进行强制性的处理,而 RuntimeException 下的子类在编写代码异常不需要强制性的处理,由用户自己选择,如果不处理并且产生异常将交由JVM负责处理
            常见的 RuntimeException :ArithmeticException,NullPointerException,ClassCastException,NumberFormatException

自定义异常类
        在java中的异常类之中提供有大量的类型,但是这些提供的类型几乎都是与语法有关的异常类型,缺少业务有关的类型
        例如:现在输入某一个的成绩,成绩如果超过了100,那么就应该产生一个与之对应的异常处理。
        但是这样的异常往往都需要开发者自己来进行设计,正因如此才需要使用到自定义异常类的概念
        对于异常类型主要有两类:Exception, RuntimeException
        范例:设计一个成绩的异常

    class ScoreException extends Exception{
public ScoreException(String mag){
super(mag)
}
}
public class Abnormal{
public static void main(String aegs){
double score = 101.0:
if(score>100.0){
throw new ScoreException("成绩大于100分了");
}
}
}


总结:
        1.几种组合: try...catch,try...catch...finally...throws...throw:
        2. RuntimeException 与 Exception 的区别
        3.异常的处理流程:引用对象的传递过程

菜鸡的Java笔记 第三十 - java 异常的捕获及处理的更多相关文章

  1. 菜鸡的Java笔记 第三十六 - java 函数式编程

    StudyLambda    Lambda 指的是函数式编程,现在最为流行的编程模式为面向对象,很多的开发者并不认可面向对象,所以很多的开发者宁愿继续使用 C 语言进行开发,也不愿意使用java,c+ ...

  2. 菜鸡的Java笔记 第三十四 Annotation

    Annotation        多例模式特点:            1. 多例类可以有多个实例            2. 多例类必须自己创建自己的实例,并管理自己的实例,和向外界提供自己的实例 ...

  3. 菜鸡的Java笔记 第三十五 接口定义增强

    接口定义增强        在java从一开始到现在接口之中的核心组成部分:抽象方法与全局常量,但是随着技术的不断发展,用户在使用过程之中发现了有一些问题        如果说现在有一个接口经过了长年 ...

  4. 菜鸡的Java笔记 第三十二 - java 静态导入的实现

    静态导入的实现        为了理解静态导入的操作产生的动机,下面通过一个具体的代码来观察        范例:现在有一个 Import 的类,这个类中的方法全部都是 static 方法 packa ...

  5. 菜鸡的Java笔记 第三十七 - java 线程与进程

    线程与进程        线程与进程的区别                最早的的时候DOS 系统有一个特点:只要电脑有病毒,那么电脑就死机了,是因为传统的DOS 系统属于单进程的操作系统       ...

  6. 菜鸡的Java笔记 第三十三 - java 泛型

    泛型 GenericParadigm        1.泛型的产生动机        2.泛型的使用以及通配符        3.泛型方法的使用                JDK1.5 后的三大主 ...

  7. “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. “全栈2019”Java多线程第三十四章:超时自动唤醒被等待的线程

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

随机推荐

  1. quartz学习

    quartz是一个作业调度框架,用于指定工作(作业)在指定时间执行——定时工作. quartz的核心接口有: Scheduler接口:Scheduler是job的执行对象,用于工作的执行. Job接口 ...

  2. php 通过变量 来调用函数

    <?php function fun() { echo 'fun'; } $a = 'fun'; $a(); ?> 复制代码 上面的$a变量就是fun()函数,调用$a()和调用fun() ...

  3. workerman 的属性

    <?php /** * Created by PhpStorm. * User: zeopean * Date: 2016-08-26 * Time: 16:35 */ use Workerma ...

  4. Windows下使用Git和GitHub&period;com

    1.首先介绍一下什么是Git和GitHub       Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理.在推出后,Git在其它项目中也取得了很大 ...

  5. 分享关于学习new BufferedWriter&lpar;&rpar;方法时常遇到的一个无厘头的问题

    今天在学习IO的过程中,关于处理流BufferedWriter的使用时,遇到了一个很犯二但是又会让初学者经常没有避免的问题,百度后才发现有人和我一样二,这还是对java基础掌握得不牢固的原因啊. 首先 ...

  6. 完毕port&lpar;CompletionPort&rpar;具体解释 - 手把手教你玩转网络编程系列之三

       手把手叫你玩转网络编程系列之三    完毕port(Completion Port)具体解释                                                    ...

  7. 写好你的JavaScript

    关于 微信公众号:前端呼啦圈(Love-FED) 我的博客:劳卜的博客 知乎专栏:前端呼啦圈 前言 在实际工作中,我们应该经常会看到一些功能上没有问题,但编码风格和规范却十分糟糕的代码,这往往会让人不 ...

  8. 关于Unity里动态加载图片

    Resources.Load 使用该方法可以动态加载资源 过程: 1.首先需要在Project面板里创建一个名为Resources的文件夹(名字必须是这个 不能写错啊) 2.把要加载的游戏对象放到该目 ...

  9. js取数组最大值的四种方式

    var arr = [7,2,0,-3,5];1.apply()应用某一对象的一个方法,用另一个对象替换当前对象 var max = Math.max.apply(null,arr);console. ...

  10. 创建react项目

    npm搭建React项目 React官网提供最简便的方法是使用create-react-app npx create-react-app my-app cd my-app npm start 也可以自 ...