JavaSE 异常抛光解析

异常

异常指的是程序中的不正常现象,一般异常都是由第三方数据的使用造成的。java中每种异常现象都会有一个对应的异常类。java对异常的处理方式就是终止程序。异常机制其实是为了帮助我们找到程序中的问题。异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。

异常体系

java将程序中遇到的每一种异常现象都封装成一个对应的异常类。然后将众多的异常抽取成一个异常体系。而整个异常系统又分为: 错误 和 异常两种现象。

  • java.lang.Throwable:异常体系的顶层父类。其下有两个子类:
    • java.lang.Error: 错误体系的顶层父类。严重的错误Error,无法处理,只能预先避免,好比绝症。
    • 比如开辟数组空间的个数过多,导致内存不够使用。这不是代码的问题,而是硬件不足造成的。
    • java.lang.Exception:异常体系中的顶层父类。程序中的异常就是指java.lang.Exception。
    • 程序中一旦产生了异常的现象,程序开发人员可以通过代码纠正(就好比生活中身体出现异常,可以通过药物或其他方式进行治疗)。异常是必须要处理的(好比发烧、感冒、阑尾炎)。

小结

Throwable是异常体系的根类。针对于程序中的异常,分为:错误(Error)和异常(Exception)。

Error:无法处理,尽量避免。

Exception:必须处理。

异常的产生过程解析

当我们操作了数组不存在的下标时,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。

步骤

  1. 创建一个数组的工具类,类中定义静态方法getElement(),通过提供的数组和下标获取对应的元素。
  2. 创建测试类,在类中定义数组,开辟空间个数3。
  3. 通过数组工具类名调用获取数组元素的方法,并传递创建的数组和4下标。
  4. 运行程序,查看结果。

实现

工具类

public class ArrayTools {
    // 对给定的数组通过给定的角标获取元素。
    public static int getElement( int[] arr, int index ) {
        int element = arr[index];
        return element;
    }
}

测试类

public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        int num = ArrayTools.getElement(arr, 4)
        System.out.println("num = " + num);
        System.out.println("over");
    }
}

上述程序执行过程图解:

小结

当程序中的数据产生异常之后,JVM会识别当前异常。然后将异常的信息进行封装,抛给程序的调用者,让调用者基于获取的异常信息进行处理。而如果没有对异常进行处理,则异常最后会由JVM进行处理。JVM处理的方式就是终止程序,并将异常的信息打印到控制台。

异常分类和处理

目标

程序中需要开发人员处理的异常指的是Exception,这类异常一旦出现,我们就要对代码进行更正,修复程序。

异常(Exception)的分类:分为 编译时期异常 和 运行时期异常。

  • 编译时期异常:在编译时期,就会检查。如果没有处理异常,则编译失败。( 如日期格式化异常 )
    • Exception :编译时期异常顶层父类。除特殊的子类外,其下所有子类都属于编译时期异常类。
  • 运行时期异常:在运行时期,检查异常。运行中发现异常则程序停止运行。编译时期,不会被编译器检测。
    • RuntimeException :Exception的一个特殊子类,运行时期异常顶层父类。其下的所有子类都属于运行时期的异常类。如:下标越界。
  • 异常的处理
    • 编译时期异常:编译时就会编译报错,因此必需要处理。只有处理之后才能编译成功,产生class文件。然后才能够让程序运行。
    • 运行时期异常:编译时期不会检查,运行时期检测。如果运行时真的出现了异常现象JVM会结束程序。针对于运行时期异常:可以处理,也可以不用处理,一般不处理。

步骤

  1. 在演示异常的类中创建意思日期字符串。
  2. 创建DateFormat对象,并调用parse()方法解析日期字符串(编译异常)
  3. 对比分析编译时期和运行时期异常类。

实现

public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        /*
            运行时期的异常,只有在运行时才会检测。编译时期不会检测。
         */
        int num = ArrayTools.getElement(arr, 4);
        System.out.println("num = " + num);

        // 创建日期字符串
        String time = "2018-12-12";
        // 创建DateFormat对象,解析日期字符串
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        /*
            调用parse()方法,解析日期字符串。
            当前方法上有一个编译时期的异常,针对于编译时期异常一定要进行处理。
            否则编译会报错。
          */
        Date parse = df.parse(time);
        System.out.println("parse = " + parse);

        System.out.println("over");
    }
}

小结

异常分为

编译时期异常 : 顶层父类Exception,编译时期检测,必须要处理的异常。

运行时期异常 : 顶层父类RuntimeException,运行时期检测,选择性处理的异常。一般不处理。

异常的处理

Java异常处理的五个关键字:try、catch、finally、throw、throws

抛出异常throw

方法上经常要接收调用者传递的数据,而方法本身对于传递的数据通常会有范围的限定,超出范围即为不合法的数据。因此在程序开发时,通常需要对方法上传递的参数进行合法性的判断。如果传递的数据不合法,我们则需要将数据非法(异常)的具体信息告知给方法的调用者。就好像生活中看医生,如果身体出现了异常的现象,医生会告诉我们具体的病情一样的道理。

在java中如果需要将数据异常的信息告诉给方法的调用者知道,则需要使用throw关键字 。代码体现就是将异常信息进行封装,然后使用throw关键字抛出给调用者。

  • throw关键字:用来抛出一个异常对象,将这个异常对象传递给调用者,并结束当前方法的执行。步骤:
    1. 创建一个异常对象。封装异常的一些提示信息( 信息可以自己编写 )。
    2. 通过throw关键字,将这个封装异常信息的对象告知给调用者。

使用格式:

throw new 异常类名(参数);

例如:

throw new NullPointerException("要访问的arr数组不存在");

throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

注意:异常在抛出时可以分为两种情况,即:抛出 编译时期异常 或者 运行时期异常。并且抛出的异常对象必须是异常体系所属的类。

学习完抛出异常的格式后,我们通过下面程序演示下throw的使用。

步骤

  1. 定义getElement()方法,传递int数组和下标。
  2. 使用if判断,传递的下标数据是否合法,如果不合法则抛出下标越界异常并提示异常信息。
  3. 实现功能,通过下标获取数组中下标对应的元素。
  4. 调用方法,查看结果。

实现

public class ThrowDemo {
    public static void main(String[] args) {
        //创建一个数组 
        int[] arr = {2,4,52,2};
        //根据索引找对应的元素 
        int index = 4;
        int element = getElement(arr, index);

        System.out.println(element);
        System.out.println("over");
    }
    /*
     * 根据 索引找到数组中对应的元素
     */
    public static int getElement( int[] arr,int index ){ 
       	//判断  索引是否越界
        if( index < 0 || index > arr.length - 1 ) {
             /*
             判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
             这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 
              */
             throw new ArrayIndexOutOfBoundsException("哥们,下标越界了~~~");
        }
        int element = arr[index];
        return element;
    }
}

小结

程序中如果产生了问题,需要使用throw关键字,将描述问题的类即异常进行抛出,也就是将问题返回给该方法的调用者。 格式:

throw new 异常类("异常信息!");

使用throw关键字抛出的异常对象,必须是属于异常体系的类对象。

注意:throw语句下面,不能写其他语句,否则会因为无法被执行而导致程序编译报错。

那么对于调用者来说,该怎么处理呢?基于运行时期异常,一般是不处理。而针对于编译时期异常,一种是进行捕获处理,另一种就是继续将问题声明出去,使用throws声明处理。

Objects非空判断

还记得我们学习过一个类Objects吗,曾经提到过它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,将对象为null的值,进行了抛出异常操作。

  • public static T requireNonNull(T obj):查看指定引用对象不是null。

查看源码发现这里对为null的进行了抛出异常操作:

public static <T> T requireNonNull( T obj ) {
    if ( obj == null )
      	throw new NullPointerException();
    return obj;
}

步骤

  1. 定义getLength()方法,接收String类型数据,计算其长度并打印。
  2. 对传递的字符串数据进行合法性的判断,判断是否为空,如果为空则抛出空指针异常。
    • 先使用传统的if判断,然后使用Object类中的requireNonNull()方法进行操作。
  3. 对比两种操作方式的区别。

实现

public static void getLength( String str ) {
    // if判断传递的字符串数据是否合法(为null)
    /*if( str == null ) {
              throw new NullPointerException("字符串不能为null");
          }*/

    // Objects静态方法requireNonNull()方法进行判断
    Objects.requireNonNull( str ,"字符串不能为null");

    System.out.println(str + "长度为:" + str.length() );
}

小结

对传递的引用类型数据,一般都需要做控制针的判断。Objects类中的requireNonNull()方法是专门用来判断引用类型的参数是否为空的。

声明异常 throws

throw关键字抛出的异常分为编译时期异常 和 运行时期异常。针对于运行时期的异常可以不用作任何的操作处理。但如果抛出的是编译时期的异常,则必须要进行处理。否则编译报错,程序无法运行。而对编译时期异常的处理方式主要分为: 异常声明 和 异常捕获。

  • 声明异常:将异常标识出来,报告给程序的使用者。让程序的使用者在程序的编译时期即可发现异常信息。
    • 例如生活中,超市售卖商品,有些商品快过期了,超市会在快过期的商品上贴上标签。让顾客在买商品时就能看到商品的异常信息。
  • throws关键字的作用就是在方法上进行异常的声明,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常( 继续抛出异常 )。

声明异常格式:

访问权限修饰符 返回值类型 方法名(参数) throws 异常类名1 , 异常类名2…{   }

注意:如果方法中抛出多个编译时期异常,也可以只声明多个异常的父类异常。

步骤

演示方法异常的声明。

  1. 定义read( String path )方法。
  2. 对传递path变量进行判断
    1. 判断是否为null,为null则抛出空指针异常。(运行异常)
    2. 判断path路径地址是否为"a.txt",如果不是则抛出FileNotFoundException文件不存在异常(编译异常)
    3. 判断path路径地址是否为"b.txt" , 如果不是则抛出IOException异常对象(编译时期异常)
  3. 针对编译时期异常进行声明处理。
  4. 调用read()方法,并将main()方法被动接受到的异常继续声明到main()方法上。

实现

public class ThrowsDemo {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        read("a.txt");
    }

    public static void read(String path) throws FileNotFoundException, IOException {
        if (!path.equals("a.txt")) {//如果不是 a.txt这个文件 
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
        if (!path.equals("b.txt")) {
            throw new IOException();
        }
    }
}

小结

如果通过throw关键字抛出的编译时期的异常,那么就必须要对异常进行处理 , 声明异常就是对编译时期异常的一种处理方式:将程序的调用者在编译时期就可以看到异常信息。需要使用throws关键字对异常进行声明:

访问权限修饰符 返回值类型 方法名(参数) throws 异常类名1, 异常类名2...{}

注意:

  1. throws关键字只能用在方法的声明处。
  2. throws声明的类必须是异常体系中的类。
  3. 如果方法中抛出多个编译时期异常,那么也必须在方法上继续声明抛出多个异常,或多个异常的父类异常。
  4. 哪个方法调用了声明异常的方法,那么当前方法就会被动接受被调用方法上的异常。

捕获异常try…catch

使用异常的声明时,如果方法中真的出了现异常现象,那么JVM就会将程序终止。造成的后果是后续的代码无法继续正常的执行。而如果希望即使出现了异常的现象,也能让后续的代码继续执行,可以使用异常的另外一种处理方式:捕获异常。

在java中使用:try-catch语句捕获异常。

  • 捕获异常:对异常语句,针对性进行捕获,捕获到异常信息后,可以对捕获的异常进行自定义方式的处理。
    • 提示:JVM对异常的默认处理方式就是终止程序的运行。
  • 捕获异常自定义处理的方式,Throwable中的常用方法:
    • public void printStackTrace(): 打印异常的详细信息。
      包含了异常的类型,异常的原因,异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
    • public String getMessage() : 获取发生异常的原因。提示给用户的时候,就提示错误原因。

捕获异常语法如下:

try{
     编写可能会出现异常的代码
} catch ( 异常类型 e ){
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}
/*
	try:该代码块中编写可能产生异常的代码。尝试运行程序。
	catch:用来捕获程序中产生的异常,并实现对捕获到的异常进行处理。
	异常类型 e:用来接收throw关键字抛出的异常对象(也就是异常的信息,因为异常对象中封装了异常的信息)
*/

注意:try 和 catch都不能单独使用,必须连用。

步骤

演示使用try-catch语句捕获异常。

  1. 定义read( String path )方法。
  2. 对传递path变量进行判断
  3. 判断path路径地址是否为"a.txt",如果不是则抛出FileNotFoundException文件不存在异常(编译异常)
  4. 针对编译时期异常使用try-catch语句进行捕获处理。
  5. 调用read()方法。

实现

public class TryCatchDemo {
    public static void main(String[] args) {
        // 调用方法  如果方法是声明处理的异常,此处可以对方法进行捕获处理。
        read("b.txt");
        System.out.println("over");
    }
    // 演示捕获 异常
    public static void read(String path)  {
        //如果不是 a.txt这个文件
        if (!path.equals("a.txt")) {
          	// 对异常进行捕获处理
            try {
                throw new FileNotFoundException("文件不存在");
            }catch (FileNotFoundException e ) {
                // 自定义处理异常,输出打印异常被捕获
                System.out.println("异常被抓住了!!!");
            }
        }
        // 打印传递的参数
        System.out.println("path = " + path );
    }
}

小结

针对于程序中的编译时期异常,除了声明处理之外,也可以使用捕获处理。

声明处理:遇到异常JVM会终止程序的运行。

捕获处理:遇到异常之后,可以自定义对异常处理。不用结束程序。

try{
  可能发生异常的代码;
}catch( 异常类型 e ) {
  对捕获的异常进行自定义的处理;
}
注意: try-catch 不能单独使用。

提示:如果方法上的异常是声明处理的,那么调用异常方法的方法就会被动接收到异常。此时可以使用声明的方式继续将异常声明出去,也可以使用try-catch语句进行捕获。

finally 代码块

在使用try-catch代码块处理异常时,有可能发生异常的代码是书写在try代码块中。而在异常代码的下面,有时还会有其他代码需要执行。但是一旦真有异常产生,程序会跳转到catch代码块中执行代码。那么异常代码后续的代码是无法执行的。而如果异常语句下面的代码无论是否有异常产生都需要执行,则可以使用finally代码块完成。

finally代码块:在finally代码块中存放的代码都是一定会被执行的。

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

finally的语法:

try{
  有可能发生异常的代码;
} catch( 异常类型 e ) {
  自定义处理异常;
} finally {
  无论是否有异常产生,都需要执行的代码;
}

注意:finally不能单独使用。

我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。

步骤

演示finally代码块。

  1. 定义read( String path )方法。
  2. 对传递path变量进行判断
  3. 判断path路径地址是否为"a.txt",如果不是则抛出FileNotFoundException文件不存在异常(编译异常)
  4. 针对编译时期异常使用throws进行声明处理。
  5. 调用read()方法,并捕获处理此方法。
  6. 在read()方法下面,写一个一定要执行的输出语句。
  7. 传递异常数据,产生异常。查看输出语句是否会被执行。
  8. 将一定要执行的输出语句书写到finally代码块中再次运行程序。查看结果。

实现

public class Demo {
    public static void main(String[] args) {
        try {
            readFile("b.txt");
            // 无论是否有异常产生都需要执行的代码,写在finally代码块中
            //System.out.println("无论是否有异常产生我都需要执行...");
        } catch ( FileNotFoundException e ) {
            // 表示异常被抓住并且已经处理了
            System.out.println("异常被抓住了..");
          	// 结束当前JVM,此时finally语句不会执行 
            // System.exit(0); // 开发中一般不会使用
        } finally {
            // 无论是否有异常产生都需要执行的代码,写在finally代码块中
            System.out.println("无论是否有异常产生我都需要执行...");
        }
    }
    // 演示finally代码块的使用
    public static void readFile ( String file ) throws FileNotFoundException {
       if( !file.equals("a.txt") ) { // 如果不是a.txt文件
           // 假设如果不是a.txt文件,则认为该文件不存在,抛出对应的异常
           throw new FileNotFoundException("文件不存在!");
       }
    }
}

小结

finally代码块是用来书写无论是否发生异常都需要执行的代码语句。只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。

finally代码块注意事项

因为finally代码块中的代码一定会被执行,所以一般不会将需要返回结果的代码写在finally代码块中。因为会永远返回finally代码块中的结果。要尽量避免类似情况的发生。

步骤

  1. 定义getA()方法,返回int类型。
  2. 在方法内部定义变量 int a = 10;
  3. 定义try-catch-finally代码块
  4. 在try代码块中直接返回变量 a;
  5. 在finally代码块中给变量a重新赋值,返回在返回。
  6. 调用getA()方法。获取返回的结果并查看。

实现

public class Demo {
    public static void main(String[] args) {
        // 调用方法,获取返回值
        int i = gatA();
        System.out.println("i = " + i); // 100
    }
    // 演示捕获 异常
    public static int gatA() {
        // 定义变量 a
        int a = 10;
        try{
            return a;
        }catch ( Exception e ) {
            System.out.println(e);
        } finally {
            a = 100;
            return a;
        }
    }
}

小结

如果finally代码块中有return语句,那么一定会返回finally代码块中的结果。尽量避免类型情况的发生。

自定义异常

自定义异常类概述

在java中,一个异常类表示了一种异常现象。而这些异常类都JDK内部定义好的。但是实际开发中,也会出现很多异常现象,并且这些异常现象是JDK中没有定义的。此时我们可以根据自己业务的异常情况来自定义异常类。

  • 自定义异常类 : 在开发中根据自己业务的异常情况自定义的异常类。
  • 自定义异常类注意事项 :
    • java中规定,只有Throwable异常体系的类,才能进行抛出或声明等异常处理。
      • 所以自定义的异常类必须继承异常类体系。
    • 自定义一个编译期异常:自定义类 并继承于java.lang.Exception。
    • 自定义一个运行时期的异常类:自定义类 并继承于java.lang.RuntimeException。

自定义异常类格式

访问权限修饰符 class 异常类名 extends Exception | RuntimeException {
  // 空参构造
  public 异常类名(){}
  // 异常信息提示构造  使用字符串数据提示异常的具体信息
  public 异常类名( String message ){
  	super( message );
  }
}

步骤

定义一个注册异常的编译时期异常类。

  1. 自定义LoginException类。
  2. 继承Exception类。
  3. 在类中定义空参构造 和 异常信息提示构造。

实现

// 业务逻辑异常
public class LoginException extends Exception {
    /**
     * 空参构造
     */
    public LoginException() {
    }

    /**
     *
     * @param message 表示异常提示
     */
    public LoginException(String message) {
        super(message);
    }
}

小结

当程序中出现异常现象时,我们需要将异常信息封装,然后抛出给程序的调用者知道。代码体现就是创建异常现象对应的异常类对象,然后使用throw关键字抛出。而如果程序中的异常现象没有对应的异常类时,我们可以根据项目情况自己定义异常类。而java中规定,只有属于异常体系的类才能使用处理异常的关键字进行处理。所以自定义的异常类要继承异常体系类。

如果希望是编译时期异常则继承:Exception类。

如果希望是运行时期异常则继承:RuntimeException类。

自定义的异常类中需要提供一个空参构造,一个异常信息提示的构造方法。

自定义异常的练习

步骤

要求:我们模拟登陆操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

  1. 定义一个静态的成员字符串数组,并在数组中声明几个账号,用于模拟数据库。
  2. 定义checkUsername( String name )方法,判断注册的账号是否存在
  3. 遍历取出模拟数据库数组中的数据
  4. 使用数据库中已经存在的账号和注册的账号进行比较。如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

实现

public class Demo {
    // 模拟数据库已经存在的账号
    private static String[] names = {"zhangSan","liSi","wangWu"};

    public static void main(String[] args) {
        // 调用方法
        try{
            // 可能出现异常的代码
            checkUsername("liSi");
            // 没有发生异常,则表示注册成功
            System.out.println("账号注册成功!!");
        }catch ( LoginException e ) {
            // 处理异常
            e.printStackTrace();
        }
    }

    // 判断当前账号是否存在
    public static boolean checkUsername ( String unams ) throws LoginException {
        // 循环数据库中的所有账号
        for( String name : names ) {
            // 将取出的数据库中的账号和申请的账号比价
            if( unams.equals( name ) ) {
                // 如果进入if说明账号已经存在,则抛出账号存在异常
                throw new LoginException("账号已经存在!!");
            }
        }
        // 没有抛出异常,说明账号不存在。
        return true;
    }
}

小结

自定义异常类和JDK中定义好的异常类的使用方式都是一样的。也可以通过throw关键字抛出,通过throws关键字声明,或者通过try-catch代码块进行捕获处理。

3.3 多个异常捕获扩展

在程序中,异常现象不可能只出现一个。有时一个程序中会出现多个异常现象。多个异常的捕获,有多种不同的方式。

多个异常多次捕获多次处理

步骤

  1. 自定义异常A类,继承Exception类。
  2. 自定义异常B类,继承继承A类。
  3. 定义getAException( int a )方法,并对a进行判断,如果a==0,则抛出A异常。
  4. 定义getBException( int b )方法,并对b进行判断,如果b==0,则抛出B异常。
  5. 调用getAException( int a ),并使用捕获处理。
  6. 调用getBException( int b ),并使用捕获处理。

实现

// 自定义异常A类
public class A extends RuntimeException {
    // 空参构造
    public A() { }

    // 异常提示构造
    public A(String message) {
        super(message);
    }
}

// 自定义异常B类,继承A类
public class B extends A {
    // 空参构造
    public B() {}

    // 异常提示构造
    public B(String message) {
        super(message);
    }
}

// 演示多个异常的单独处理
public class Demo {
    public static void main(String[] args) {
        // 单独处理A异常
        try{
            getAException(0);
        }catch ( A e ) {
            System.out.println("异常被抓住了...");
            e.printStackTrace();
        }

        // 单路处理B异常
        try {
            getBException(0);
        } catch ( B e ) {
            System.out.println("异常被抓住了...");
            e.printStackTrace();
        }

        /*
            针对于运行时期异常,使用try-catch的方式进行捕获。
            好处在于: 即使有异常发生,后续代码也可以继续执行。
        */
        System.out.println("后续代码....");
    }

    // 定义方法,抛出A类异常
    public static void getAException( int a ) {
        if( a == 0 ) {
            throw new A("不能为0!");
        }
    }
    // 定义方法,抛出B类异常
    public static void getBException( int b ) {
        if( b == 0 ) {
            throw new B("不能为0!");
        }
    }
}

小结

当程序中有多个异常时,基于需求的需要我们可以对多个异常分别使用try-catch语句进行捕获处理。

多个异常一次捕获多次处理

try-catch代码块扩展语法:

try{
     编写可能会出现异常的代码
}catch( 异常类型A  e ){  //当try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch( 异常类型B  e ){  //当try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

步骤

  1. 调用getAException( int a )
  2. 调用getBException( int b )
  3. 对两个异常方法一次进行捕获
  4. 使用多个catch代码块处理捕获的多个异常

实现

// 演示多个异常的一次捕获,多次处理
public class Demo {
    public static void main(String[] args) {
        // 多个异常一次捕获,多次处理
        try{
            getAException(0);
            getBException(0);
        }catch ( B e ) {
            System.out.println("B类异常被抓住了...");
            e.printStackTrace();
        }catch ( A e ) {
            System.out.println("A类异常被抓住了...");
            e.printStackTrace();
        }
    }
    // 定义方法,抛出A类异常
    public static void getAException( int a ) {
        if( a == 0 ) {
            throw new A("不能为0!");
        }
    }
    // 定义方法,抛出B类异常
    public static void getBException( int b ) {
        if( b == 0 ) {
            throw new B("不能为0!");
        }
    }
}

小结

当程序中有多个异常时,基于需求的需要我们可以对多个异常使用try代码块一次捕获,然后使用多个catch语句分别对异常进行处理。

注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

多个异常一次捕获一次处理

步骤

  1. 调用getAException( int a )
  2. 调用getBException( int b )
  3. 对两个异常方法一次进行捕获
  4. 使用一个catch代码块处理捕获的多个异常

实现

// 演示多个异常的一次捕获,一次处理
public class Demo {
    public static void main(String[] args) {
        // 多个异常一次捕获,一次处理
        try{
            getAException(1);
            getBException(0);
        }catch ( A e ) {
            System.out.println("类异常被抓住了...");
            e.printStackTrace();
        }
    }
    // 定义方法,抛出A类异常
    public static void getAException( int a ) {
        if( a == 0 ) {
            throw new A("a不能为0!");
        }
    }
    // 定义方法,抛出B类异常
    public static void getBException( int b ) {
        if( b == 0 ) {
            throw new B("b不能为0!");
        }
    }
}

小结

当程序中有多个异常时,基于需求的需要我们可以对多个异常使用try代码块一次捕获,然后使用一个catch语句对异常进行处理。

注意:这种异常处理方式,要不catch语句中的异常类型,必须是多个异常的父类类型。能够接收任意一个被抛出的异常对象。

继承中异常注意事项

  • 如果父类声明了多个异常,子类重写父类方法时,只能声明相同的异常或者是他的子集。也可以不声明
  • 父类方法没有声明异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

步骤

  1. 创建父类,在类中定义show( int a , int b )方法.
  2. 分别对变量 a 和 b进行判断,如果结果为0,则分别抛出A,B异常。
  3. 定义子类,重写父类中的方法。验证继承中的异常注意事项。

实现

// 父类
class Fu {
    // 父类中的方法
    public void show( int a, int b ) throws A, B {
        if( a == 0 ) {
            throw new A("a不能为0!");
        }
        if( b == 0 ) {
            throw new B("b不能为0!");
        }
    }
}
class Zi extends Fu {
    // 子类重写父类中的方法
    public void show( int a , int b ) throws B {
        System.out.println("重写....");
    }
}

小结

继承中,子类重写父类方法时,父类没有声明异常。子类也不能声明异常。如果父类上有声明异常,子类重写时可以不用声明,或者声明父类异常上的子类异常,但是异常的个数不能超过父类声明的异常个数。

健康的父亲,儿子也应该健康。多病的父亲,基于进化学,儿子应该比父亲健康。

posted on 2019-04-14 22:32  OWN明哥  阅读(273)  评论(0编辑  收藏  举报

导航