Java 第27 章 : 异常的捕获及处理

课时116:认识异常对程序的影响

        Java语言提供的最为强大的支持在于异常的处理操作上。

  • 认识异常对程序的影响

        异常指的是导致程序中断执行的一种指令流。那么下面首先来观察没有异常产生的程序执行结果。

范例:没有异常产生

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        System.out.println("【2】******数学计算"+(10/2));
        System.out.println("【3】******程序执行完毕******");
    }
}
【1】******程序开始执行******
【2】******数学计算5
【3】******程序执行完毕******

        在程序执行正常的过程里面会发现,所有的程序会按照既定的结构从头到尾开始执行。

范例:产生异常

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        System.out.println("【2】******数学计算"+(10/0));
        System.out.println("【3】******程序执行完毕******");
    }
}
【1】******程序开始执行******
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at cn.mldn.JavaDemo.main(JavaDemo.java:6)

        在出现错误之后,整个的程序将不会按照既定的方式进行执行,而是中断了执行。那么为了保证程序出现了非致命错误之后程序依然可以正常完成,所以就需要有一个完善的异常处理机制,以保证程序的顺利执行。

课时118:处理多个异常

  • 处理异常

        在Java之中如果要进行异常的处理,可以使用:try、catch、finally 这几个关键字来完成,其基本的处理结构如下:

try{
   //可能出现异常的语句
}[catch(异常类型 异常对象){
   //异常处理
}catch(异常类型 异常对象){
   //异常处理
}catch(异常类型 异常对象){
   //异常处理
}...][finally{
  不管异常是否处理都要执行;
}]

        在此格式之中可以使用的组合为:try...catch、try...catch...finally、try...finally。

范例:处理异常

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        try {
            System.out.println("【2】******数学计算" + (10 / 0));
        } catch (java.lang.ArithmeticException e) {
            System.out.println("【C】处理异常:" + e);  //处理异常
        }
        System.out.println("【3】******程序执行完毕******");
    }
}
【1】******程序开始执行******
【C】处理异常:java.lang.ArithmeticException: / by zero
【3】******程序执行完毕******

        此时可以发现现在即便出现了异常,程序也可以正常执行完毕,所以此时的设计属于一个合理设计,但是有一个问题出现了。此时在进行异常处理的时候直接输出的是一个异常类的对象,那么对于此对象如果直接打印(调用toString())所得到的异常信息并不完整,那么如果想要获得非常完整的异常信息,则可以使用异常类中提供的printStackTrace()方法。

范例:获取完整异常信息

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        try {
            System.out.println("【2】******数学计算" + (10 / 0));
        } catch (java.lang.ArithmeticException e) {
            e.printStackTrace();
        }
        System.out.println("【3】******程序执行完毕******");
    }
}

        对于异常的处理格式也可以在最后追加一个finally程序块,表我是异常处理后的出口,不管是否出现异常都执行。

范例:使用finally语句

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        try {
            System.out.println("【2】******数学计算" + (10 / 1));
        } catch (java.lang.ArithmeticException e) {
            e.printStackTrace();
        }finally{
            System.out.println("【F】不管是否出现异常我都执行");
        }
        System.out.println("【3】******程序执行完毕******");
    }
}

        此时程序中有异常执行finally,没有异常也执行finally。

课时118:处理多个异常

  • 处理多个异常

        很多时候在程序执行的过程中可能会产生若干个异常,那么这种情况下也可以使用多个catch进行异常的捕获。现在假设通过初始化的参数来进行两个数学计算数字的设置。

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            System.out.println("【2】******数学计算" + (x / y));
        } catch (java.lang.ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("【F】不管是否出现异常我都执行");
        }
        System.out.println("【3】******程序执行完毕******");
    }
}

        那么对于此时的程序就有可能产生三类异常:

  • 【未处理】程序执行的时候没有输入初始化参数(java JavaDemo):java.lang.ArrayIndexOutOfBoundsException
程序执行结果:

【1】******程序开始执行******

【F】不管是否出现异常,我都会执行。

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:0

             at JavaDemo.main(JavaDemo.java:5)

 

 

 

  • 【未处理】输入的数据不是数字(java JavaDemo a b):java.lang.NumberFormatException;
程序执行结果:

【1】******程序开始执行******

【F】不管是否出现异常,我都会执行。

Exception in thread"main" java.lang.NumberFormatException:For input string:"a"

              at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

              at java.base/java.lang.Integer.parseInt(Integer.java:652)

              at java.base/java.lang.Integer.parseInt(Integer.java:770)

              at JavaDemo.main(JavaDemo.java:5)

 

 

 

 

 

  • 【已处理】输入的被除数为0:java.lang.ArithmeticException;
程序执行结果:

【1】******程序开始执行******
【F】不管是否出现异常我都执行
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
    at cn.mldn.JavaDemo.main(JavaDemo.java:7)

【3】******程序执行完毕******

 

 

 

 

        现在即便有了处理语句,但是如果没有进行正常的异常捕获,那么程序也会导致中断(finally的代码依然执行),所以在这样的情况下就必须进行多个异常的捕获。

范例:修改程序代码

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            System.out.println("【2】******数学计算" + (x / y));
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        } finally {
            System.out.println("【F】不管是否出现异常我都执行");
        }
        System.out.println("【3】******程序执行完毕******");
    }
}

        此时我们开发者都已经明确的知道有哪些异常了,那有何必非要用个异常处理呢?直接多写点判断不就可以了。

课时119:异常处理流程

        在进行异常处理的时候,如果将所有可能已经明确知道要产生的异常都进行了捕获,虽然你可以得到非常良好的代码结构,但是这种代码编写时非常麻烦的,所以现在要想进行合理的异常就必须清楚再异常产生之后程序到底做了哪些处理。

1、在程序运行的过程之中才会产生异常,而一旦程序执行中产生了异常之后将自动进行指定类型的异常类对象实例化处理;

2、如果此时程序之中并没有提供有异常处理的支持,则会采用JVM默认异常处理方式,首先进行异常信息的打印,而后直接退出当前的程序;

3、此时程序中如果存在有异常处理,那么这个产生的异常类的实例化对象将会被try语句所捕获;

4、try捕获到异常之后与其匹配的catch中的异常类型进行依次的比对,如果此时与catch中的捕获异常类型相同,则认为应该使用此catch进行异常处理,如果不匹配则继续匹配后续的catch类型,如果没有任何的catch匹配成功,那么就表示该异常无法进行处理;

5、不管异常是否处理最终都要执行finally语句,但是当执行完成finally的代码之后就会进一步判断当前的异常是否已经处理过了,如果处理过了,则继续向后执行其他代码,如果没有处理则交由JVM进行默认的处理。

        通过分析可以发现在整个异常处理流程之中实际上操作的还是一个异常类的实例化对象,那么这个异常类的实例化对象的类型就成为了理解异常处理的核心关键所在,在之前接触过了两种异常:

AirthmeticException:ArrayIndexOutOfBoundsException:

java.lang.Object

  java.lang.Throwable

    java.lang.Exception

      java.lang.RuntimeException

        java.lang.AirthmeticException

java.lang.Object
  java.lang.Throwable
    java.lang.Exception
      java.lang.RuntimeException
        java.lang.IndexOutOfBoundsException
          java.lang.ArrayIndexOutOfBoundsException

 

 

 

 

 

 

        可以发现在程序之中可以处理的异常的最大的类型就是Throwable,而打开Throwable可以观察在此类中提供有两个子类:

  • Error:此时程序还未执行出现的错误,开发者无法处理;
  • Exception:程序中出现的异常,开发者可以处理,真正在开发之中所需要关注的就是Exception;

        通过分析可以发现异常产生的时候会产生异常的实例化对象,那么按照对象的引用原则,可以自动向父类转型,那么如果按照这样的逻辑,实际上所有的异常都可以使用Exception来处理。

范例:简化异常处理

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        System.out.println("【1】******程序开始执行******");
        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            System.out.println("【2】******数学计算" + (x / y));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("【F】不管是否出现异常我都执行");
        }
        System.out.println("【3】******程序执行完毕******");
    }
}

        当你不确定产生哪些异常的时候,这种处理方式是最方便的。但是如果这样处理也会产生一个问题,这种异常的处理形式虽然方便,但是它描述的错误信息不明确,所以分开处理异常是一种更加明确的处理方式。

        在以后进行多个异常同时处理的时候要把捕获范围大的异常放在捕获范围小的异常之后。

课时120:throws关键字

        通过之前的程序可以发现,在执行程序的过程之中有可能会产生异常,但是如果说现在你定义了一个方法,实际上就应该明确的告诉使用者,这个方法可能会产生何种异常,那么此时就可以在方法的声明上使用throws关键字来进行异常类型的标注。

范例:观察throws的使用

package cn.mldn;

class MyMath {
    //这个代码执行的时候可能会产生异常,如果产生异常调用处处理
    public static int div(int x, int y) throws Exception {
        return x / y;
    }
}

public class JavaDemo {
    public static void main(String args[]) {
        try {
            System.out.println(MyMath.div(10, 0));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

        主方法本身也是一个方法,那么实际上主方法也可以继续向上抛出。

范例:在主方法上继续抛出异常

package cn.mldn;

class MyMath {
    //这个代码执行的时候可能会产生异常,如果产生异常调用处处理
    public static int div(int x, int y) throws Exception {
        return x / y;
    }
}

public class JavaDemo {
    public static void main(String args[]) throws Exception {
        System.out.println(MyMath.div(10, 0));
    }
}

        如果主方法继续向上抛出异常,那么就表示此异常将交由JVM负责处理。

课时121:throw关键字

        与throws对应的还有throw关键字,此关键字的主要作用在于表示手工进行异常的抛出,即:此时将手工产生一个异常类的实例化对象,并且进行异常的抛出处理。

范例:观察throw的使用

package cn.mldn;
public class JavaDemo {
    public static void main(String args[]) {
        try {  //异常对象不再是由系统生成的,而是由手工定义的
            throw new Exception("自己抛着玩的对象。");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

面试图:请解释throw与throws区别?

  • throw:是在代码块中使用的,主要是手工进行异常对象的抛出;
  • throws:是在方法定义上使用的,表示将此方法中可能产生的异常明确告诉给调用处,由调用处进行处理。

课时122:异常处理模型

  • 异常处理的标准格式

        现在已经学习完成了大部分的异常处理格式:try、catch、finally、throw、throws,那么这些关键字在实际的开发之中往往会一起进行使用,下面通过一个具体的程序来进行分析。

        现在要求定义一个可以实现除法计算的方法,在这个方法之中开发要求如下:

  • 在进行数学计算开始与结束的时候进行信息提示;
  • 如果在进行计算的过程之中产生了异常,则要交给调用处来处理。
package cn.mldn;
class MyMatch {
    //异常交给被调用处处理则一定要在方法上使用throws
    public static int div(int x, int y) throws Exception {
        int temp = 0;
        System.out.println("【START】");
        try {
            temp = x / y;
        } catch (Exception e) {
            throw e;  //向上抛异常对象
        } finally {
            System.out.println("【END】");
        }
        return temp;
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        try {
            System.out.println(MyMatch.div(10, 2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

        对于此类操作实际上可以简化,省略掉catch与throw操作。

package cn.mldn;
class MyMatch {
    //异常交给被调用处处理则一定要在方法上使用throws
    public static int div(int x, int y) throws Exception {
        int temp = 0;
        System.out.println("网络连接资源开始");
        try {
            temp = x / y;
        } finally {
            System.out.println("网络连接资源关闭");
        }
        return temp;
    }
}
public class JavaDemo {
    public static void main(String args[]) {
        try {
            System.out.println(MyMatch.div(10, 0));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

        在以后实际开发过程之中,这种异常的处理格式是最为重要的,尤其是当与一些资源进行访问操作的时候尤其重要。

课时123:RuntimeException

        通过之前的分析可以发现只要方法后面带有throws往往都是告诉用户本方法可能产生的异常是什么,所以这个时候来观察一段代码。

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        int num = Integer.parseInt("123");  //把字符串的数据转整型
        System.out.println(num);
    }
}

        打开Integer类中的parseInt()方法的定义来观察:public static int parseInt​(String s)throws NumberFormatException;

        这个方法上明确的抛出了一个异常,但是我们在处理的时候并没有强制性的要求处理观察一下NumberFormatException类的继承结构,同时也观察数学异常类的继承结构:

AirthmeticException:NumberFormatException:

java.lang.Object

  java.lang.Throwable

    java.lang.Exception

      java.lang.RuntimeException

        java.lang.AirthmeticException

java.lang.Object
  java.lang.Throwable
    java.lang.Exception
      java.lang.RuntimeException
        java.lang.IllegalArgumentException
          java.lang.NumberFormatException
 

 

 

 

 

        如果现在所有的程序执行上只要使用了throws定义的方法都必须要求开发者进行手工处理,那么这个代码的编写就太麻烦了,所以在设计的过程之中,考虑到代码编写的方便,所以提供有一个灵活的可选的异常处理父类“RuntimeException”,这个类的异常子类可以不需要强制性处理。

面试题:请解释RuntimeException与Exception的区别?请例举出几个你常见的RuntimeException;

  • RuntimeException是Exception的子类;
  • RuntimeException标注的异常可以不需要进行强制性的处理,而Exception异常必须强制性处理;
  • 常见RuntimeException异常:NumberFormatException、ClassCastException、NullPointerException。

课时124:自定义异常类

        在JDK之中提供有大量的异常类型,但是在实际的开发之中可能这些异常类型未必够你所使用的,你不可能所有的设计里面都只是抛出Exception,所以这个时候就需要考虑自定义异常类。但是对于自定义异常也有两种实现方案:继承Exception、继承RuntimeException。

范例:实现自定义异常

package cn.mldn;

class BombException extends Exception {
    public BombException(String msg) {
        super(msg);
    }
}

class Food {
    public static void eat(int num) throws BombException {
        if (num > 10) {
            throw new BombException("吃太多了,肚子爆了");
        } else {
            System.out.println("可正常开始吃,不怕吃胖。");
        }
    }
}

public class JavaDemo {
    public static void main(String args[])throws BombException {
        Food.eat(11);
    }
}

        在以后的项目开发过程之中,会接触到大量的自定义异常处理,如果遇见了你不清楚的异常,最简单的方式就是通过搜索引擎查询一下异常可能产生的原因。

课时125:assert断言

  • assert关键字

        从JDK1.4开始追加了断言的功能,确定代码执行到某行后,一定是所期待的结果。在实际的开发之中,对于断言而言,并不一定是准确的,也有可能出现偏差,但是这种偏差不应该影响程序的正常执行。

范例:断言的使用

package cn.mldn;

public class JavaDemo {
    public static void main(String args[]) {
        int x = 10;
        //中间经过许多的x变量的操作步骤
        assert x == 100 : "x的内容不是100";
        System.out.println(x);
    }
}

        如果现在要想执行断言,则必须在程序执行的时候加入参数:

java -ea JavaDemo

Exception in thread "main" java.lang.AssertionError:x的内容不是100

                at JavaDemo.main(JavaDemo.java:5)

 

 

 

所以在Java里面并没有将断言设置为一个程序必须执行的步骤,需要特定环境下才可以开启。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值