Java知识点总结【7】异常

目录

一、什么是异常

二、防御式编程(引入异常的原因)

1.什么是防御式编程

2.防御式编程的两种具体代码的体现形式

三、异常的具体语法

1.try

2.catch

3.finally

4.throw

5.throws

四、Java的异常体系

1.什么是Java的异常体系

2.受查异常和非受查异常

3.自定义异常


一、什么是异常

异常是程序运行过程中出现的一种错误。异常的种类有很多种,分别代表不同的含义,一旦出现某个异常,就会明确的告诉程序猿出现异常的原因,所以是帮助我们解决问题的一种很好的手段。

二、防御式编程(引入异常的原因)

1.什么是防御式编程

在程序运行过程中,如果一个程序现在没有错误,不代表以后没有错误,不代表在别人的机子上运行也没有错误...所以,为了能够尽量避免这些问题,在写程序的时候,我们就要首先去多方面考虑会出现什么样的问题,也就是"未言胜先言败",针对这些问题,再想出相应的解决办法。这样,就是防御式编程。

2.防御式编程的两种具体代码的体现形式

以王者为例:

①LBYL(不使用异常):Look Before Your Leap:操作之前就做充分的检查,检查完上一步之后,再来执行下一步的操作,如果上一步失败,就不继续执行了。

boolean ret=login();
if(!ret){
    //处理登录失败
    return;
}
ret=startMatch();
if(!ret){
    //处理匹配失败
    return;
}
ret=enterRoom();
if(!ret){
    //处理进入房间失败
    return;
}
ret=chooseHero();
if(!ret){
    //处理选择英雄失败
    return;
}

EAFP(使用异常):It's Easier to Ask Forgiveness than Permission:先斩后奏

try{
    login();
    startMatch();
    enterRoom();
    chooseHero();
    ...    
}catch(XXX){
    //处理异常的代码
}

分析:如果执行到某一步出错,就会抛出异常。一旦抛出异常,就会进入catch这样的代码中执行异常处理逻辑。

上面两种方式中,可以发现第二种更好,它将程序的正常流程和处理错误分离开了,更清楚更容易理解!

三、异常的具体语法

1.try

放置可能会抛出异常的代码

2.catch

放置用来处理异常的代码,搭配try来使用,当try出现异常时,就会进入catch中执行

例①

int[] a=null;
System.out.println(a[0]);
System.out.println("hello");

运行结果

分析:如果代码中出现了异常,并且没有使用try catch,异常就会由JVM自己来处理程序就会直接被终止,不会再往下走。

例②

try{
    System.out.println("try中异常之前的代码");
    int[] a=null;
    System.out.println(a[0]);
    System.out.println("try中异常之后的代码");
}catch(NullPointerException e){
    System.out.println("catch中的代码");
}

System.out.println("hello");

运行结果

分析:try catch中的执行顺序:先按顺序执行try中的代码,如果出现异常—>进入到catch执行(try中剩余的代码就不执行了)—>当catch也执行完毕之后,就会执行后续的代码,程序并没有异常终止。

例③

catch中的异常的类型需要和抛出的异常类型匹配,才能够正确的处理,否则执行不到catch中的逻辑,那么还是会由JVM自己去处理。

try{
    System.out.println("try中异常之前的代码");
    int[] a=null;
    System.out.println(a[100]);
    System.out.println("try中异常之后的代码");
}catch(NullPointerException e){
    System.out.println("catch中的代码");
}

System.out.println("hello");

运行结果

分析:a[100]这是数组下标越界异常,不是空指针异常,所以不匹配,catch就不进行处理了,就会由JVM去处理,程序直接被终止。

例④

使用catch可以捕获多个异常(|

try{
    System.out.println("try中异常之前的代码");
    int[] a=null;
    System.out.println(a[100]);
    System.out.println("try中异常之后的代码");
}catch(NullPointerException | ArrayIndexOutOfBoundsException e){
    System.out.println("catch中的代码");
}

System.out.println("hello");

分析:使用 | 就可以把多个异常的类型并列起来,相当于“逻辑或”,抛出这若干个异常中的任何一个,都会触发catch。

例⑤

还有一个更绝的方法来捕获多个异常(Exception

try{
    System.out.println("try中异常之前的代码");
    int[] a=null;
    System.out.println(a[100]);
    System.out.println("try中异常之后的代码");
}catch(Exception e){
    System.out.println("catch中的代码");
}

System.out.println("hello");

分析:Exception是一个级别很高的父类,空指针异常和数组下标越界异常都是Exception的子类,在进行catch匹配的时候,不一定要求类型就是完全一模一样的,如果抛出的异常时catch的参数的异常的子类,也是可以的(本质上是向上转型)。一般不建议用Exception,杀伤力太大,强行把所有异常都是用同一个逻辑来处理了。

3.finally

放置在finally中的代码一定会被执行到,一般用于异常处理完毕之后的收尾工作,比如关闭文件等等。

例①

使用finally放到try catch的后面,finally中的逻辑保证是一定会被执行到的

try{
    System.out.println("try中异常之前的代码");
    int[] a={1,2,3};
    System.out.println(a[100]);
    System.out.println("try中异常之后的代码");
}catch(NullPointerException e){
    System.out.println("catch中的代码");
}finally {
    System.out.println("hello");
}

分析:finally中的逻辑无论是前面的代码中是否触发异常,都会执行到。a[100],是数组下标越界异常,但是在catch中参数的类型是空指针异常,类型不匹配,那就还是由JVM去处理了,finally中的代码依然被执行到了。所以文件关闭的操作就可以放到finally中。

注意:finally中不建议写return语句,如果try中有return语句,finally中也有return语句,那么就会执行finally中的return语句,而不会执行try中原有的return语句。

例②

使用finally回收资源确实比较靠谱,但是代码写起来比较麻烦,还可以使用Java1.7 提供的try with resource机制来完成这个操作。(使用try负责回收资源

try(Scanner sc=new Scanner(System.in)){
    int num=sc.nextInt();
    System.out.println(num);
}catch (InputMismatchException e){
    System.out.println("输入类型不匹配异常");
}

分析:将 Scanner 对象在 try 的 ( ) 中创建, 就能保证在 try 执行完毕后自动调用 Scanner 的 close 方法。

4.throw

主动抛出一个异常对象

示例①

public class Test2 {
    public static int divide(int x,int y){
        if(y==0){
            throw new ArithmeticException("此处抛出了一个算数异常");
        }
        return x/y;
    }
    public static void main(String[] args) {
        try{
            int ret=divide(10,0);
        }catch(ArithmeticException e){
            e.printStackTrace();
        }
    }
}

printStackTrace()方法可以获取到异常的一些具体信息。

5.throws

标注方法可能会抛出某些异常

public class Test2 {
    public static int divide(int x,int y) throws ArithmeticException{
        if(y==0){
            throw new ArithmeticException("此处抛出了一个算数异常");
        }
        return x/y;
    }
    public static void main(String[] args) {
        try{
            int ret=divide(10,0);
        }catch(ArithmeticException e){
            e.printStackTrace();
        }
    }
}

分析:throws标注divide方法,可以明确的知道divide方法可能会抛出ArithmeticException这个异常,提醒方法的调用者小心处理这些异常~

四、Java的异常体系

1.什么是Java的异常体系

用来描述Java标准库中提供的异常类都有哪些,并且分成那几个类型。

注意:

  • 图上的向上的箭头,其实就是"继承"或者"实现"这样的关系。
  • Error是系统级别的异常,JVM内部使用的,普通程序员不应该使用Error这个体系。
  • Exception是应用级别的异常,普通程序员要使用这一组系列。

2.受查异常和非受查异常

Java语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为 非受查异常所有的其他异常称为 受查异常。

非受查异常:

  • 可以不显示处理
  • Error:出现Error的情况一定是非常严重的情况,一般就是JVM机挂了,此时,作为应用级别的程序猿,毫无办法,只能任其自生自灭。
  • RuntimeException:一些最常见的异常,影响没有很大。

受查异常:

  • 如果某个方法中抛出了这个异常,那么就必须对这个异常进行显示的处理
  • 包括两种方案:①直接try catch ②使用throws声明可能会抛出这个异常。

3.自定义异常

自定义异常通常会继承自Exception(受查)或者RuntimeException(非受查)。

示例:实现一个简单的控制台版用户登陆程序, 程序启动提示用户输入用户名密码,如果用户名密码出错, 使用自定义异常的方式来处理

自定义的NameException异常:

public class NameException extends Exception{
    public NameException(String str){
        super(str);
    }
}

自定义的PasswdException异常:

public class PasswdException extends Exception{
    public PasswdException(String str) {
        super(str);
    }
}

Test类

import java.util.Scanner;

public class Test {
    private static String userName="ttlxbb";
    private static String userPassword="5201314";
 //主类
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入姓名");
        String name=sc.next();
        System.out.println("请输入密码");
        String password=sc.next();
        try{
        login(name,password);
        }catch(NameException | PasswdException e){
            e.printStackTrace();
        }
    }
 //login类
    public static void login(String name,String password)throws NameException, PasswdException {
        if(!name.equals(userName)){
            throw new NameException("用户名输入错误");
        }
        if(!password.equals(userPassword)){
            throw new PasswdException("用户密码输入错误");
        }
        System.out.println("登录成功!");
    }
}

运行结果

分析:login方法后面throws NameException异常和PasswdException异常,表示login方法可能会抛出这两个异常。然后就会将这个异常对象抛出,交给上级调用者处理。

猜你喜欢

转载自blog.csdn.net/weixin_43939602/article/details/113112840