java-异常 的 使用和介绍

异常

 我们在编写java程序的时候,会遇到很多的异常,如:

除以0:

        //除以0-异常
        System.out.println(10 / 0);

 此处是一个算数异常。

数组访问越界:

        //数组访问越界-异常
        int[] array = {1,2,3};
        System.out.println(array[100]);

 数组下标越界异常

 访问空对象:

public class text1 {

    public int num = 10;

    public static void main(String[] args) {
        //访问空对象
        text1 T = null;
        System.out.println(T.num);
    }
}

 我们发现,在java中不同的错误,会有不同的异常。

有些异常的,在报异常之后还会报一个信息,如上述数组越界时报错异常,在报出数组月结之后,还报了一个数字:

 这个数字代表的就是,在数组越界这个异常中的,具体的异常,上述的100,表示,这个数组没有下标为 100 的元素,而此时访问了数组 下标为 100 的元素。

下面的 "  at text1.main(text1.java:19)  "  就是异常信息栈:

 这个栈中,可能不会只有一条信息,可能会有多条信息,我们在找错误信息的时候,要从第一行蓝色部分开始依次排查错误,对蓝色部分进行点击,就可以跳到报异常的地方。

那么既然是栈,就是后进先出的,

那么所谓异常就是,在程序在运行时,出现错误时,告知调用者的一种机制。

关键词-运行时

假如我们在 拼写 System.out.println() 这个函数的时候,拼写错了,那么在编译时期就会报错,这时“编译期”的出错。

而像上述三个例子,他们编译期已经通过,已经得到了class文件了,接下来再由 JVM 执行过程中出错,这就叫运行时出错。

而上述我们也知道了,异常有很多种,如果我们查看文档,可以发现,异常其实是一个类,在这类中实现很多异常的类,而这个类有一个父类- Throwable 类,这个类是java中所有错误和异常的父类:

     

主要实现了,编译时异常和运行时异常的两个类:

运行时异常,又叫做 非受查异常  ; 编译时异常,有叫做 受查异常。

异常的种类有很多,不同的异常会有不同的处理方法,那么我们不可能都记住,所以我们就要进行防御性编程。

防御性编程

 代码中时常出现错误,由此我们就要然程序出现异常的时候,在报运行时错误之前,就告知程序员,那么此处主要有两种方式:

LBYL: Look Before You Leap. 在操作之前就做充分的检查.
EAFP: It's Easier to Ask Forgiveness than Permission. "事后获取原谅比事前获取许可更容易". 也就是先操作,遇到问题再处理。


LBYL风格的代码(不使用异常):

        boolean ret = false;
        ret = 登陆游戏();   // 登录成功放回 true
        if (!ret) {
            处理登陆游戏错误;
            return;
        }
        ret = 开始匹配();
        if (!ret) {
            处理匹配错误;
            return;
        }
        ret = 游戏确认();
        if (!ret) {
            处理游戏确认错误;
            return;
        }


     ··················

 捕获异常

EAFP风格的代码(使用异常):
使用 try {}  catch {}  ,来处理异常。

 java中处理异常的语法:

try
{

  //可能出现异常代码
}catch(参数:异常的类型1 e){
  捕获try中可能出现的异常
}catch(参数:异常的类型2 e){
  捕获try中可能出现的异常
}catch(参数:异常的类型3 e){
  捕获try中可能出现的异常
}

···········

可以用 catch 来捕获 try 中的异常,而捕获上面异常,我们发现catch可以传入参数,这个参数是异常的类型,当然我们也可以处理多个异常,我们在try下调用多个catch来处理多个异常。

      

 如这个例子,在报出异常之后,那么后序的 “hello” 字符串是不会打印的:

 那么如果我们想在报出遗产之后,依然执行后面的代码,那么我们就要对这个异常一下处理:

        text1 T = null;
        try{
            System.out.println(T.num);
        }catch (NullPointerException e)
        {
            System.out.println("捕获到了数组越界异常");
        }

        System.out.println("hello");

 上述的参数就是,以异常作为类名,然后创建了 e 这个变量。

这个代码的打印结果是:

 我们发现,我们执行了我们写的异常处理代码,后面的 “hello” 也被打印了。

当我们对异常进行处理之后,那么后序代码将会继续执行。

我们上述在catch中传入了一个参数,那么这个参数该如何使用呢?

这个参数是一个对象,那么我们就可以使用这个类中的方法:

        text1 T = null;
        try{
            System.out.println(T.num);
        }catch (NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到了数组越界异常");
        }

        System.out.println("hello");

 调用栈:

方法之间是存在相互调用关系的, 这种调用关系我们可以用 "调用栈" 来描述. 在 JVM 中有一块内存空间称为 "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈。

 我们上述使用了 e.printStackTrace();这个方法,我们就可以在屏幕上打印之前报的异常了:

 我们来把程序处理异常和不处理异常来进行对比:

 但是,处理异常需要对应异常的类型,也就是说,此时是数组越界抛出的异常,那么在catch中就要对这个异常进行捕获,如果在catch中没有对这个异常代码进行捕获异常的话,或者说捕获的异常不是产生的异常的话,那么也会把这个异常交给 JVM 进行处理,此时程序也不会往下执行:

        text1 T = null;
        try{
            System.out.println(T.num);
        }catch (ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
            System.out.println("捕获到了数组越界异常");
        }

        System.out.println("hello");

如这个例子,产生的异常是访问空指针,但是没有写这个异常,写的是数组访问越界的异常,那么此时这个异常没有进行处理,也会把这个异常交给 JVM 进行处理,那么此时的 ”hello“ 字符串不会进行打印:

 用try catch 捕获异常的时候,需要注意的问题:

  • catch块当中,一定要捕获相应的异常,如果程序抛出的异常在catch块当中,不能被捕获,那么就会交给JVM来处理。
  • 可以用catch来捕获多个异常
  • 可以用Exception 父类来一次捕获多个异常

用 Exception 父类捕获异常:

        text1 T = null;
        try{
            System.out.println(T.num);
        }catch (ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
            System.out.println("捕获到了数组越界异常");
        }catch(NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到空指针异常");
        }catch(Exception e)
        {
            System.out.println("捕获到Exception异常");
        }

        System.out.println("hello");

因为catch是从上往下捕获的,所以此时还是报的 空指针异常。

那么现在我们把 Exception 放在第一个catch位置:

 我们发现下面的 catch 异常都报错了,一下的异常都不能捕获到了。

这是因为只要是异常,都会被 Exception 给捕获到,那么我们在下面定义的任何异常都没有用了。

我们查看官方文档可以发现,我们之前所调用的异常都是这个 Exception 的子类:

 那么此时这些异常就都可以用父类 Exception来实现。

那么如果我们使用 Exception 来捕获异常的话,那么我们后面就不能在使用其他的异常了。

        try{
                int[] array = {1,2,3};
                System.out.println(array[100]);
        }catch(Exception e)
        {
            e.printStackTrace();
            System.out.println("捕获到Exception异常");
        }

打印:

        text1 T = null;
        try{
            System.out.println(T.num);
        }catch(Exception e)
        {
            e.printStackTrace();
            System.out.println("捕获到Exception异常");
        }

打印:

  由于 Exception 类是所有异常类的父类,因此可以用这个类型表示捕捉所有异常。而且,catch 进行类型匹配的时候, 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类对象。如刚才的代码, NullPointerException 和 ArrayIndexOutOfBoundsException 都是 Exception 的子类, 因此都能被捕获到。

 但是我们不建议 直接使用 Exception来进行捕获异常,因为不同的异常我们有不同的解决方式,那么我们使用 Exception 来捕获不知道具体是哪一个异常。

但是我们使用上述的多个 catch 来捕获的话,代码会显得很长,如果有多个异常用相同的处理方式的话,那么我们可以这样写:

        catch (ArrayIndexOutOfBoundsException | NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到了数组越界异常");
        }


            System.out.println("hello");

这样的话,我们就可以使用一个catch 来判断多个异常,比如上述我们可以把 数组越界和 访问空指针 这两个异常进行一个 catch 处理。

我们可以对两个异常进行报错,使用 printStackTrace () 这个方法,同样可以在屏幕上打印 这两种异常,但是需要注意的是,如果两种异常都存在的话,那么他会首先报 第一个 异常,第二个异常不会报:

        text1 T = null;
        try{
            System.out.println(T.num);
            int[] array = {1,2,3};
            System.out.println(array[100]);

        }
        catch (ArrayIndexOutOfBoundsException | NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到了数组越界异常");
        }

                System.out.println("hello");

输出:

 我们发现,他只报了第一个空指针的异常,因为空指针在 数组越界代码的上面,而往下的数组越界的异常并没有报出来。

 如果我们使用多个catch也是一样的:

        text1 T = null;
        try{
            System.out.println(T.num);
            int[] array = {1,2,3};
            System.out.println(array[100]);

        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
            System.out.println("捕获到了数组越界异常");
        }
        catch(NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到空指针异常");
        }

        System.out.println("hello");

输出:

 我们之前说过,catch是从上往下执行的,但是此时的 NullPointerException 空指针异常是在 ArrayIndexOutOfBoundsException 数组访问越界的catch 的下一个。也就说,如果两个异常都发生的话,那么先处理的是 try {} 中的第一个异常,而后的异常不会处理。

 其实上述的不只是异常,是后序的代码都不会被执行:

        text1 T = null;
        try{
            System.out.println(T.num);
            System.out.println("后序代码");
            System.out.println("执行后序代码");

        }
        catch(NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到空指针异常");
        }

输出:

 我们发现,在上述空指针异常代码之后的代码都没有被执行,屏幕上没有打印 “后序代码” 和 “执行后序代码” 这两个字符串都没有打印。

 所以我们在 try {} 代码块中,尽量不要写多的代码,进行少写多判断。

异常的处理方式有很多:

  • 对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
  • 对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
  • 对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试

 finally语法

 其实上述不是 try {} 的使用异常的基本语法,以下才是 基本语法:

try{

//有可能出现异常的语句;

}
catch (异常类型 异常对象) 
{
    //发生对应异常的处理
} 
...  // 可以有多个catch 

finally {
  //异常的出口
}

所谓 finally 的意思就是,无论上述的代码有没有抛出异常,在finally 块当中的代码是一定会执行的。而且执行的顺序是,当try {} 当中的代码执行完,如有异常抛出,处理完之后再执行,也就说会在 整个异常处理的代码块中 最后执行。

        text1 T = null;
        try{
            System.out.println(T.num);
            System.out.println("后序代码");// 不会执行
        }
        catch(NullPointerException e)
        {
            e.printStackTrace();
            System.out.println("捕获到空指针异常");
        }
        finally
        {
            System.out.println("finally块的代码一定会执行");
        }

        System.out.println("hello");

输出:

 finally的作用主要就是,做一些善后的工作,比如关闭资源等等,比如开了文件对象,那么在使用完文件对象之后要把这个文件给关闭。

 例子:

    public static int func(){
        int[] array = {1,2,3};

        try
        {
            System.out.println(array[100]);
            return 1;
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
            System.out.println("数组访问越界异常");
        }
        finally
        {
            return -1;
        }
    }

    public static void main(String[] args) {
        System.out.println(func());//-1
    }

我们这例子实现的是,如果这个代码块抛出异常,那么就返回 -1 ,如果正常执行就返回 1。那么肯定,上述例子的数组是访问越界的,那么结果就返回的是 -1 。

但是,如果我们上述在 try {} 块中的代码没有抛出异常,那么我们的预期是返回1,但是结果返回的是 - 1 :

            System.out.println(array[1]); // 2
            return 1;

我们发现 2 是打印的数组的 1 号下标的元素,而 -1 是func()方法的返回值,我们发现不是 1 而且 -1。

这也是我们一直在提及的问题:finally{} 块中的代码是一定要被执行的,也就说即使我们在 try{} 块中有 return 类似的结束代码的执行的语句,但是在 finally {} 块当中的也是要被执行的。除了方法返回值,在循环中使用 break ,continue这些也是一样的: 

    public static void main(String[] args) {
        int count = 0;
        while(count <= 10)
        {
            try
            {
                System.out.println("循环第" + count + "次");
                if(count >= 3)
                {
                    break;
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.out.println("Exception 异常");
            }
            finally
            {
                System.out.println("循环第" + count + "次");
            }
            count++;
        }
    }

发现 try中的和 finally中的 输出结果次数是一样的。

 如果我们不使用 finally {} 打印 ,直接在 try 之后 进行打印:

    public static void main(String[] args) {
        int count = 0;
        while(count <= 10)
        {
            try
            {
                System.out.println("循环第" + count + "次");
                if(count >= 3)
                {
                    break;
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
                System.out.println("Exception 异常");
            }
            System.out.println("循环第" + count + "次");
            count++;
        }
    }

我们发现,当 count = 3 的时候,在try 外打印的次数少了一次;

如左图,第三次少打印一次。

 那么同样的,在catch中的return之前,也要执行 finally {}中的代码:

    public static int func(){
        int[] array = {1,2,3};

        try
        {
            System.out.println(array[1]);
        }
        catch(ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
            System.out.println("数组访问越界异常");
            return 1;
        }
        finally
        {
            return -1;
        }
    }

    public static void main(String[] args) {
        System.out.println(func());//-1
    }

我们发现,此时的打印结果还是 -1。

finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return
 

因为 finally 当中代码是永远会被执行的,当我们不用实现特殊效果的时候,我们一般不在finally当中写return类似的可以终止 代码的 代码。

 利用 try 自动回收资源:

 我们之前说过,finally{}块当中一般用来回收资源,但是这是手动回收,当我们需要自动回收资源的时候就可以在 try() 的() 当中来写入创建的对象的代码,此时,当try{} 执行完毕之后会自动的调用对应的 close()方法:

如这个例子,当我们在try()中创建了一个 Scanner 的对象用于输入,当try {} 执行完之后,我们再调用这个对象,那么这个对象将不能再被调用:

    public static void main(String[] args) {
        try (Scanner sc = new Scanner(System.in)) {
            int num = sc.nextInt();
            System.out.println("num = " + num);
        } catch (Exception e) {
            e.printStackTrace();
        }

        int num2 = sc.nextInt();
    }

 此时报错:无法解析的外部符号 ‘sc'。

这里说的 try执行完,指的就是 try指向完,后面的 catch和finally都无法使用这个 sc:

 其实在Idea中,他会自己帮我们判断,我们所书写的代码,是否有更好的形式去书写,比如下面这个代码:

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in)
        try  {
            int num = sc.nextInt();
            System.out.println("num = " + num);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            sc.close();
        }
    }

此时try上有一个 “加深底色”的样式:

 此时我们鼠标放到try上,按下: Alt + Enter  , 会出现以下界面:

 我们点击这个按键,就会自动的给我们修改为更好的代码形式:

 如上图就是自己生成的。

 其实 try{}块当中的变量也是不能在 try外部来进行访问的:

 这是因为 在 try当中定义的是 局部变量(-这些变量属于方法/块/构造函数并在其中声明/定义。这些变量的范围位于方法(或块或构造函数)之内,执行后将被销毁 -)。

 因此,当try 块中代码执行完之后,其中的局部变量的生命周期也到了,就不能再使用了。

 如果在发生异常的本方法中没有处理异常,那么这个异常就沿着栈向上传递:

    public static void func1(){
        int[] array = {1,2,3};
        System.out.println(array[100]);
    }

    public static void main(String[] args) {
        func1();
    }

比如这个代码,在func1 方法中发生了异常,但是没有对这个异常进行处理,那么就会把这个异常抛给调用这个方法的函数中,也就是抛给main函数,如果这个main函数也没有处理,那么就会交给 JVM 栈进行处理,那么如果交给 JVM 栈进行处理,那么后序的代码就不能再执行了。

如果我们要想在执行,就要在func1函数中,或者是在 main函数中来处理这个异常:

    public static void func1(){
        int[] array = {1,2,3};
        System.out.println(array[100]);
    }

    public static void main(String[] args) {
        try
        {
            func1();
        }
        catch (ArrayIndexOutOfBoundsException e)
        {
            e.printStackTrace();
            System.out.println("数组访问越界");
        }
    }

总结-异常处理的流程

  •  程序先执行 try 中的代码
  • 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
  • 如果找到匹配的异常类型, 就会执行 catch 中的代码
  • 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
  • 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  • 如果上层调用者也没有处理的了异常, 就继续向上传递.
  • 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

 抛出异常

 除了 Java 内置的类会抛出一些异常之外, 程序猿也可以手动抛出某个异常. 使用 throw 关键字完成这个操作。

    public static void main(String[] args) {
        int x = 0;
        if(x == 0)
        {
            throw new ArithmeticException("抛出 除0 异常");
        }
        int num = 10 / x;
        System.out.println(num);
    }

输出结果:

 当我们在使用 throw 关键字的时候,要想抛出一个异常,那么就要使用new关键字去创建这个异常的对象,因为我们前面也讲过,所谓各种异常就是 很多个类。那么我们要想使用这个类,就要创建一个对象。

 其中的参数,就会在异常之后打印在屏幕上:

 使用throw 需要注意的是:当我们在方法中使用throw 的时候,调用者大概率是不会知道我定义的方法中有一个 throw 抛出异常的,当调用者使用我定义的方法,满足我定义的throw 条件之后,那么就会抛出异常。

那么调用者在查看源代码的时候,如果这个方法实现的行数不是很多,那么他会很容易就发现我定义的throw 抛出异常,但是如果这个方法实现了很多行,那么调用者就不容易发现我实现的 throw 抛出异常。

如这个例子,实现这不知道第二个参数不能传入 0 :

    public static int divit(int x,int y)
    {
        if(y == 0)
        {
            throw new ArithmeticException("y == 0 除数不能为0");
        }
        return x/y;
    }

    public static void main(String[] args) {
        int ret = divit(20,0);
        System.out.println(ret);
    }

 抛出异常:

所以,如果我们在类或者是方法中实现某个 throw 抛出异常之后,要在这个的头部使用 throws 关键字来申明一下:

    public static int divit(int x,int y) throws ArithmeticException
    {
        if(y == 0)
        {
            throw new ArithmeticException("y == 0 除数不能为0");
        }
        return x/y;
    }

向调用者表明这个方法可能会抛出 ArithmeticException这个异常,那么调用者在使用这个方法的时候就要使用 捕获异常的方式来调用 这个方法
 

    public static void main(String[] args) {
        try{
            int ret = divit(20,0);
            System.out.println(ret);
        }catch (ArithmeticException e)
        {
            e.printStackTrace();
            System.out.println("除数不能为0");
        }
    }

受查异常:

class Person implements Cloneable{
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

 此时就发生了受查异常,这种异常是在编译时期产生的,那么我们在程序运行之前必须处理这个异常。

我们知道,要想一个自定义的类实现 clone()复制,那么就要让这个类实现Cloneable接口,然后再重写的 如上一样重写 clone()方法。

然后,当我们在方法中调用这个 clone() 方法的时候,要进行一些处理,之前是说要在 方法后声明这个 异常:

我们在此处,声明这异常代表的是,哪个方法声明了这个异常,哪个方法就去处理这个异常。

但是我们不建议这样做,这样做,如果抛出异常,那么到最后也是交给 JVM 去处理这个异常,后面的代码不会继续执行。

我们要自己捕获异常:
可以手动写,在IDea也可以 Alt + Enter :

 点击这个,就会自动的帮我们写:

    public static void main(String[] args){
        Person person = new Person();
        try {
            Person person1 = (Person)person.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            System.out.println("clone 异常");
        }
    }

Java 异常体系

Java 内置了丰富的异常体系, 用来表示不同情况下的异常.
下图表示 Java 内置的异常类之间的继承关系:
 

上图,红色的类异常都是 受查异常,蓝色的是 非受查异常。所谓受查异常就是在编译时期 产生异常,是在程序运行时 之前产生的异常,这种异常是必须处理的,不然程序不能运行;而非受查异常就是 在程序运行时产生的异常,这种异常不需要处理,是根据数据或使用不同所产生的异常,程序运行不一定会发生。

  •  顶层类 Throwable 派生出两个重要的子类, Error 和 Exception
  • 其中 Error 指的是 Java 运行时内部错误和资源耗尽错误. 应用程序不抛出此类异常. 这种内部错误一旦出现,除了告知用户并使程序终止之外, 再无能无力. 这种情况很少出现.
  • Exception 是我们程序猿所使用的异常类的父类.
  • 其中 Exception 有一个子类称为 RuntimeException , 这里面又派生出很多我们常见的异常类
  • NullPointerException , IndexOutOfBoundsException 等

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

 对于受查异常,一定要用 try {} catch{} 来捕获异常。一般不建议在本方法中 直接声明这个异常。

 自定义异常类

 虽然java中已经有很丰富的异常类了,但是有时候我们也需要定义一些 符合我们实际情况的异常类。

 因为所谓的异常类,也只是一个类,我们点开 ArithmeticException  异常的源代码,发现这个 异常继承了 RuntimeException 这个父类。而且在这个类中实现了两个构造方法,一个带参数,一个不带参数。

那么我们可以这样来实现一个 自定义异常类:

    class MyException extends RuntimeException{
        public MyException(){
            super();
        }

        public MyException(String message)
        {
         super(message);
        }
    }

这样我们就实现了一个异常类。

然后我们来使用一个这个异常类:

    public static void Exception_fun(int num) throws MyException{
        if(num == 10)
        {
            throw new MyException("num = 10");
        }
    }

    public static void main(String[] args) {
        try
        {
            Exception_fun(10);
        }
        catch(MyException e)
        {
            e.printStackTrace();
        }
    }

此处我们向,之前的例子一样,在方法中抛出异常。

输出结果:

 我们发现,和之前的是一样的。

我们发现我们这个自定义异常类,继承的是 RuntimeException 类,我们也可以继承 Exception 类,继承这两种类,实现的异常是不一样的:

当我们继承 RuntimeException 类的时候,那我们定义的类是 非受查异常,如上述;

当我们继承 Exception 类的是偶,那么我们定义的类就是 受查异常:

class MyException extends Exception{
    public MyException(){
        super();
    }

    public MyException(String message)
    {
        super(message);
    }
}

 我们发现此时,满足异常条件,在程序没有运行的时候,IDEA已经报错:

在创建自定义异常类的时候需要注意的是:

  • 要继承一个异常父类,这个父类通常是 Exception 或 RuntimeException 。
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常
     

 例子2:

模拟实现简单用户登录:

假设我们先不用 自定义异常类来实现:

    public class Test {
        private static String userName = "admin";
        private static String password = "123456";
        public static void main(String[] args) {
            login("admin", "123456");
        }
        public static void login(String userName, String password) {
            if (!Test.userName.equals(userName)) {
                // TODO 处理用户名错误
            }
            if (!Test.password.equals(password)) {
                // TODO 处理密码错误
            }
            System.out.println("登陆成功");
        }
    }

那么就要用if一个一个来判断。

如果使用 自定义异常类来实现的话:

class UserError extends Exception {
    public UserError(String message) {
        super(message);
    }
}
class PasswordError extends Exception {
    public PasswordError(String message) {
        super(message);
    }
}

public static Text{
    
     private static String userName = "admin";
     private static String password = "123456";

    public static void main(String[] args) {
            try {
            login("admin", "123456");
            } catch (UserError userError) {
                userError.printStackTrace();
            } catch (PasswordError passwordError) {
                passwordError.printStackTrace();
            }
}
    public static void login(String userName, String password) throws UserError,
        PasswordError {
        if (!Test.userName.equals(userName)) {
            throw new UserError("用户名错误");
        }
        if (!Test.password.equals(password)) {
            throw new PasswordError("密码错误");
        }
        System.out.println("登陆成功");
}


}


自定义异常最好是继承于  Exception ,这样的话,就必须去处理。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chihiro1122

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值