java 异常进阶 多异常和自定义异常详解

简介: java 多异常,子父类异常,自定义异常详解。

目录

一、前言:

二、多异常的处理:

       Δ前言:

       1.处理方式一:

               代码演示:

       2.处理方式二:

               代码演示:

       3.处理方式三:

               代码演示:

三、PS:finally代码块中存在return的问题:

       代码演示:

四、子父类异常:

       1.子父类异常的关系:

       2.代码演示①:

       3.代码演示②:

五、自定义异常:

       1.介绍:

       2.格式:

       3.注意事项:

       4.代码演示:

               ①自定义异常类:

               ②测试自定义异常:

六、完结撒❀:


一、前言:

       大家好,这篇博客是对java异常基础篇的一个内容延申和补充,主要和大家分享一下多异常的三种处理方式自定义异常介绍自定义异常的定义和使用等等,感谢阅读。              

二、多异常的处理:

       Δ前言:

       多异常,即代码中出现了多个异常

       多异常的处理方式:

        1.分别捕获,并分别处理

        2.一次捕获,但多次处理

        3.一次捕获,且一次处理

       1.处理方式一:

       1.分别捕获,并分别处理

       我们首先来看一下以下代码段:

int[] array= {1, 2, 11, 985, 5};
System.out.println("我想访问数组索引为5的元素:"+array[5]);

image.gif

       显然,直接运行上述代码,会报异常ArrayIndexOutOfBoundsException(数组下标越界异常)。我们再来看看另一代码段:

inti=10/0;
System.out.println("The value of i is : "+i);

image.gif

       显然,直接运行这段代码会报异常ArithmeticException(运算中的异常)。

       so,up就以这两个异常为栗,给大家演示以下多异常的处理。    

               代码演示:

packageknowledge.exception.advance;
publicclassMultipleAnomaly {
publicstaticvoidmain(String[] args) {
/*利用try...catch捕获*///1.分别捕获,并分别处理try {
int[] array= {1, 2, 11, 985, 5};
System.out.println("我想访问数组索引为5的元素:"+array[5]);
        } catch (ArrayIndexOutOfBoundsExceptionarrayIndexE) {
System.out.println(arrayIndexE);
        }
try {
inti=10/0;
System.out.println("The value of i is :"+i);
        } catch (ArithmeticExceptionariE) {
System.out.println(ariE);
        }
    }
}

image.gif

               运行结果:

image.png

       2.处理方式二:

       2.一次捕获,但多次处理

      实现方式一个try,多个catch

       Δ注意事项 :

      catch语句里面定义的异常变量,如果存在子父类关系,那么子类的异常变量必须定义在父类异常变量的上面,否则就会报错。 eg : 下面代码段:

try {
inti=10/0; 
int[] array= {1, 2, 11, 985, 5};
System.out.println("我想访问数组索引为5的元素:"+array[5]);
} catch (Exceptione) {
System.out.println(e);
} catch (ArrayIndexOutOfBoundsExceptionarrayIndexE) {
System.out.println(arrayIndexE);
}

image.gif

       报错情况如下图所示

image.png

       正如IDEA所说的,该异常已经被捕获了。        

      报错原因:

       try中如果出现了异常对象,会把异常对象抛出给catch处理,抛出的异常对象, 会从上至下依次赋值给catch中定义的变量。 如果是子类变量在前,父类变量在后(即正常情况下),因为父类异常对象本身就无法赋值给子类异常对象, 因此try会自动跳开,最终子类异常对象赋值给子类异常变量,父类异常对象赋值给父类异常变量;

        但是,

        如果是父类变量在前,子类变量在后,因为子类异常对象本身是可以赋值给父类异常对象的, 就是多态嘛。因此try不会跳开,而是将子类异常对象也赋值给了父类异常变量,这就导致 下面定义子类异常变量的catch语句是多余的语句,因此报错。

               代码演示:

packageknowledge.exception.advance;
publicclassMultipleAnomaly {
publicstaticvoidmain(String[] args) {
//2.一次捕获,但多次处理try {
inti=10/0; //直接运行这段代码会报异常ArithmeticExceptionint[] array= {1, 2, 11, 985, 5};
System.out.println("我想访问数组索引为5的元素:"+array[5]);
        } catch (ArithmeticExceptionariE) {
System.out.println(ariE);
        } catch (ArrayIndexOutOfBoundsExceptionarrayIndexE) {
System.out.println(arrayIndexE);
        }
System.out.println("---------------------------------");
/*try中若出现异常,会立刻跳转至catch,此处当执行到int i = 10 / 0; 时,就会跳转至catch语句,因此控制台只会打印出ArithmeticException*/    }
}

image.gif

               运行结果

              image.png

       3.处理方式三:

       3.一次捕获,且一次处理

       刚刚我们讲到了为什么多个catch语句中子类异常变量必须定义在父类异常变量的前面。其实,一次捕获一次处理,也是应用了这个原理。怎么做到一次处理?直接给它定义一个父类异常的异常变量不就完了么。

       直接定义一个父类异常变量,那么try无论是父类本身的异常对象抛给catch,还是它的子类对象抛给catch ,父类异常变量都可以做接收,一劳永逸!      

               代码演示:

packageknowledge.exception.advance;
publicclassMultipleAnomaly {
publicstaticvoidmain(String[] args) {
//3.一次捕获,且一次处理try {
int[] array= {1, 2, 11, 985, 5};
System.out.println("我想访问数组索引为5的元素:"+array[5]);
inti=10/0; 
        } catch (Exceptione) {
System.out.println(e);
        }
/*直接定义异常类父类Exception,可以接收绝大多数的异常对象。*/    }
}

image.gif

               运行结果:        

image.png

               注意,这里我们先产生了数组下标越界异常(ArrayIndexOutOfBoundsException),立刻便跳转到catch语句,因此控制台只打印出了ArrayIndexOutOfBoundsException。

三、PS:finally代码块中存在return的问题:

      如果finally中含有return语句,会永远只返回finally中return的结果 因此要避免此情况发生。

       PS : 注意!

       ①若finally代码块之前的catch语句中也有return语句,那么该return语句中的内容也会执行!(eg : return ++i; )。只不过执行后并不会由当前return语句返回,而是在底层用一个临时变量temp保存了该返回值,最终的返回值仍是以finally代码块中的return语句为准。若finally代码块中无return语句,才会返回去执行之前的return语句,最终返回temp变量保存的值。

       ②有时会出现try-finally的搭配,这种用法相当于没有捕获异常,因此若出现异常程序会直接崩掉。适用于“执行一段代码,不管有没有出现异常都要执行某个业务逻辑”的场景。

       代码演示:

packageknowledge.exception.advance;
/*如果finally中含有return语句,会永远返回finally中的结果因此要避免此情况发生。*/publicclassFinallyReturn {
publicstaticvoidmain(String[] args) {
intnumber=getNumber();
System.out.println("The value of number is : "+number);
    }
publicstaticintgetNumber() {
inti=11;
try {
returni;
        } catch (Exceptione) {
System.out.println(e);
        } finally {
i=5;
returni;
        }
    }
}
//最终的返回值是5

image.gif

               运行结果

image.png

四、子父类异常:

       1.子父类异常的关系:

               关于子父类的异常:

                ①如果父类中抛出多个异常,子类重写父类方法时, 抛出和父类相同的异常,或者是父类异常的子类,或者不抛出

               ②如果父类中没有抛出异常,子类重写父类该方法时, 也不可抛出异常。此时子类若产生了异常,只能做捕获处理,不能声明抛出

               人话:

                只要子类和父类保持一致,就不会出事儿。

       2.代码演示①:

       由于代码中的方法涉及到了读取文本文件,up先给大家看一下文本文件1.txt中内容,如下:

     image.png

       我们以Father1类作为第一个演示类,Father1类代码如下:

packageknowledge.exception.advance;
importjava.io.*;
publicclassFather {
publicvoidreadText() throwsIOException {
Readerreader=newFileReader("D:\\JAVA\\IDEA\\file\\1.txt");
intdata;
while ((data=reader.read()) !=-1) {
System.out.println((char)data);
        }
reader.close();
    }
publicvoidreadFile() throwsIOException {
thrownewIOException("😋抛出一个IO父类异常对象");
    }
publicvoidgetElement() throwsIndexOutOfBoundsException {
System.out.println("这是父类的方法");
    }
}
classSonextendsFather{
//1.父类抛出异常时@Override//1_①抛出和父类相同的异常publicvoidreadText() throwsIOException {
Readerreader=newFileReader("D:\\JAVA\\IDEA\\file\\1.txt");
char[] charArray=newchar[1024];  //用于读写数据的字符数组的长度最好是1024的整数倍intdata;
while ((data=reader.read(charArray)) !=-1) {
System.out.println(charArray);
        }
reader.close();
    }
//1_②抛出父类异常的子类@OverridepublicvoidreadFile() throwsFileNotFoundException {
thrownewFileNotFoundException();
    }
//1_③不抛出异常@OverridepublicvoidgetElement() {
System.out.println("这是子类的方法");
    }
}
classTest1 {
publicstaticvoidmain(String[] args) throwsIOException {
Fatherf=newFather();
Sons=newSon();
f.readText();
System.out.println("-----------------------------------");
s.readText();
f.readFile();
f.getElement();
s.readFile();
s.getElement();
    }
}

image.gif

              运行效果(GIF图):

image.png

       3.代码演示②:

       up以Father2类作为第二个演示类,来演示如果父类没有抛出异常的情况下,子类的处理情况。Father2代码如下:

packageknowledge.exception.advance;
publicclassFather2 {
publicvoidf() {
System.out.println("父类没有抛出任何异常。");
    }
}
classSon2extendsFather2 {
//2.父类没有抛出异常时@Override//    public void f() throws Exception{                 //如果此时子类声明异常,编辑器会报错。//        System.out.println("此时子类也不能抛出任何异常");//    }publicvoidf() {
System.out.println("此时子类也不能抛出任何异常");
System.out.println("假设子类重写方法时产生了异常,也只能捕获处理,不可以抛出异常对象!");
try {
thrownewException("无聊抛出一个异常对象玩玩儿😁");
        } catch (Exceptione) {
e.printStackTrace();    //这个方法在异常基础篇分享过        }
    }
}
classTest2 {
publicstaticvoidmain(String[] args) {
Father2ff=newFather2();
Son2ss=newSon2();
ff.f();
System.out.println("--------------------------------------");
ss.f();
    }
}

image.gif

               运行效果

image.png

五、自定义异常:

       1.介绍:

       java中本身已经定义了许多的异常类,但是某些情况下仍然不够我们霍霍的,所以需要我们自己去定义一些异常类。

       2.格式:

      public class ___Exception extends Exception / RuntimeException {

               //自定义的异常类内部,需要定义两个构造方法

               //一个空参构造

               //一个带参构造(说明异常信息)

       }

       3.注意事项:

       ①自定义异常类的类名一般都是以"Exception"作为结尾,以达到见名知意的效果,表示该类是一个异常类

       ②自定义的异常类,必须得继承Exception类或者RuntimeException类

       继承Exception类,表示自定义的异常类是一个编译期异常

       继承RuntimeException类,表示自定义的异常类是一个运行期异常;      

       关于编译期异常和运行期异常区别以及处理方式,我们在java异常基础篇已经讲过,此处不再赘述。

       4.代码演示:

               ①自定义异常类:

               我们创建一个RegisterException类的自定义异常类,当我们模拟注册用户时,如果用户输入的用户名异常,则可以抛出RegisterException类的异常对象。

               RegisterException类代码如下:

packageknowledge.exception.advance;
publicclassRegisterExceptionextendsException {
//1.添加一个空参构造publicRegisterException() {
super();    //默认调用父类的空参构造    }
//2.添加一个带参构造(说明异常信息)/*查看源码发现(IDEA快捷键Ctrl + b可查看类的源码),所有的异常类都会有一个带异常信息的构造方法,让父类来处理这个异常信息。*/publicRegisterException(Stringmessage) {
super(message);
    }
}

image.gif

               ②测试自定义异常:

               有了自定义的异常类,接着我们就可以创建一个测试类来测试自定义异常类,这里我们以TestRegisterException类作为演示类,需求 :模拟注册用户的操作,如果用户名已经存在,则抛出RegisterException异常并提示用户: 这个用户名已经注册过了哈

               TestRegisterException类代码如下:                

packageknowledge.exception.advance;
importjava.util.Scanner;
/*requirement : 模拟注册用户的操作,如果用户名已经存在,则抛出RegisterException异常并提示用户:这个用户名已经注册过了哈。mentality(思路) :1.首先,可以使用一个数组来保存已注册的用户名,模拟数据库。2.其次,可以使用Scanner类获取用户想注册的用户名,模拟前端页面。3.定义一个方法,对用户输入的用户名进行判断。若已存在该用户名,抛出RegisterException异常并提示用户:这个用户名已经注册过了哈。若不存在该用户名,提示用户:注册成功🌶!*/publicclassTestRegisterException {
publicstaticvoidmain(String[] args) throwsRegisterException {
//1.首先,可以使用一个数组来保存已注册的用户名,模拟数据库。String[] names= {"大伟哥", "赵鑫鑫", "杜兰特", "Cyan", "王五"};
//2.其次,可以使用Scanner类获取用户想注册的用户名,模拟前端页面。Scannersc=newScanner(System.in);
System.out.println("请输入你想注册的用户名:");
StringuserName=sc.nextLine();
ifExist(names, userName);
sc.close();
    }
//3.定义一个方法,对用户输入的用户名进行判断。publicstaticvoidifExist(String[] names, StringuserName) throwsRegisterException {
inttemp=0;
for (Stringname : names) {
if (name.equals(userName)) {
temp=-1;
thrownewRegisterException("这个用户名已经注册过了哈。");
            }
        }
if (temp==0) {
System.out.println("注册成功🌶!");
        }
/*加入这个判断条件更严谨,以不会导致在抛出异常对象后仍然打印出"注册成功🌶!"的提示信息。其实还有多种处理方式,比如在抛出异常对象后执行return语句,直接结束方法体,都可以。*/    }
}

image.gif

               运行效果如下GIF图:

image.png

六、完结撒❀:

       恭喜你看到这里,感谢阅读。

System.out.println("------------------------------------------------------------------------------------");

目录
相关文章
|
11月前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
332 1
|
11天前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
65 4
|
2月前
|
Java 数据库 C++
Java异常处理机制:try-catch、throws与自定义异常
本文深入解析Java异常处理机制,涵盖异常分类、try-catch-finally使用、throw与throws区别、自定义异常及最佳实践,助你写出更健壮、清晰的代码,提升Java编程能力。
|
4月前
|
Java 程序员 数据库连接
我们详细地讲解一下 Java 异常及要如何处理
我是小假 期待与你的下一次相遇 ~
|
3月前
|
XML 人工智能 Java
java通过自定义TraceId实现简单的链路追踪
本文介绍了如何在Spring Boot项目中通过SLF4J的MDC实现日志上下文traceId追踪。内容涵盖依赖配置、拦截器实现、网关与服务间调用的traceId传递、多线程环境下的上下文同步,以及logback日志格式配置。适用于小型微服务架构的链路追踪,便于排查复杂调用场景中的问题。
149 0
|
11月前
|
Java API 调度
如何避免 Java 中的 TimeoutException 异常
在Java中,`TimeoutException`通常发生在执行操作超过预设时间时。要避免此异常,可以优化代码逻辑,减少不必要的等待;合理设置超时时间,确保其足够完成正常操作;使用异步处理或线程池管理任务,提高程序响应性。
526 13
|
11月前
|
Java
在 Java 中,如何自定义`NumberFormatException`异常
在Java中,自定义`NumberFormatException`异常可以通过继承`IllegalArgumentException`类并重写其构造方法来实现。自定义异常类可以添加额外的错误信息或行为,以便更精确地处理特定的数字格式转换错误。
201 1
|
7月前
|
SQL Java 中间件
【YashanDB知识库】yasdb jdbc驱动集成BeetISQL中间件,业务(java)报autoAssignKey failure异常
在BeetISQL 2.13.8版本中,客户使用batch insert向yashandb表插入数据并尝试获取自动生成的sequence id时,出现类型转换异常。原因是beetlsql在prepareStatement时未指定返回列,导致yashan JDBC驱动返回rowid(字符串),与Java Bean中的数字类型tid不匹配。此问题影响业务流程,使无法正确获取sequence id。解决方法包括:1) 在batchInsert时不返回自动生成的sequence id;2) 升级至BeetISQL 3,其已修正该问题。
【YashanDB知识库】yasdb jdbc驱动集成BeetISQL中间件,业务(java)报autoAssignKey failure异常
|
7月前
|
SQL druid Oracle
【YashanDB知识库】yasdb jdbc驱动集成druid连接池,业务(java)日志中有token IDENTIFIER start异常
客户Java日志中出现异常,影响Druid的merge SQL功能(将SQL字面量替换为绑定变量以统计性能),但不影响正常业务流程。原因是Druid在merge SQL时传入null作为dbType,导致无法解析递归查询中的`start`关键字。
|
8月前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
560 14