✨hello,进来的小伙伴们,大家好呐!✨
🎁🎁系列专栏:【JavaSE】
🍱🍱本篇内容:异常的认识,异常分为几类,如何处理异常等等。
🍮🍮作者简介:一名大二即将升大三的科班编程小白,我很平凡,学会努力!
🥞🥞这里给大家推荐一个很好的刷题平台——牛客网!
在Java中,将程序执行过程中发生的不正常行为称为异常。
实例:1算术异常
- public class TestDemoX {
- public static void main(String[] args) {
- int a = 10;
- System.out.println(a/0);
- }
- }

实例2:数组越界异常
- public class TestDemoX {
- public static void main(String[] args) {
- int[] arr = {1,2,3,4,5};
- System.out.println(arr[20]);
- }
- }

实例3:空指针异常
- public class TestDemoX {
- public static void main(String[] args) {
- int[] arr1 =null;
- System.out.println(arr1.length);
- }
- }

注意这些异常都继承于Exception。
- public class Person {
- private String name;
- private String gender;
- int age;
- // 想要让该类支持深拷贝,覆写Object类的clone方法即可
- @Override
- public Person clone() {
- return (Person)super.clone();
- }
- }
在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常。
这里我们借助JavaAPI,可以知道RunTimeException以及其子类对应的异常,都称为运行时异常。

注意:编译时出现的语法性错误,不能称之为异常!
比如我们这里把println写成了prinln 这就称为语法错误,而不是异常了!
System.out.prinln();
我们刚才提到,当程序出现异常的时候,我们没去处理,那么这个时候程序就终止了,不会再继续下去!
- public class TestDemoX {
- public static void main(String[] args) {
- int a = 10;
- System.out.println(a/0);
- System.out.println("执行后续代码!");
- }
- }
可以发现,后续代码没有被执行! 这个时候异常会交给jvm来处理,程序会立即终止!

这里我们以一个游戏登录来描述:
try里面存放可能出现异常的代码;
catch里面捕获可能出现的异常;
注意:一个try可以对应多个catch。
- try {
- 登陆游戏();
- 开始匹配();
- 游戏确认();
- 选择英雄();
- 载入游戏画面();
- ...
- } catch (登陆游戏异常) {
- 处理登陆游戏异常;
- } catch (开始匹配异常) {
- 处理开始匹配异常;
- } catch (游戏确认异常) {
- 处理游戏确认异常;
- } catch (选择英雄异常) {
- 处理选择英雄异常;
- } catch (载入游戏画面异常) {
- 处理载入游戏画面异常;
- }
我们还是以刚才的算术异常来举例:
- public class TestDemoX {
- public static void main(String[] args) {
- int a = 10;
- try{
- System.out.println(a/0);
- }
- catch (ArithmeticException e){
- System.out.println("出现了算术异常!");
- }
- System.out.println("执行后续代码!");
- }
- }
运行结果:我们发现catch捕获异常之后,后续代码照样执行!

那么我们如何得知出现了何种异常呢?很简单,我们只需要通过(参数)e.printStackTrace();便可以快速的定位异常的位置。

运行结果: 
注意,这里没有同时抛出两个以上的异常的说法,就是catch捕获到异常以后,会执行catch{}里面的内容,写个例子给大家看一下:
- public class TestDemoX {
- public static void main(String[] args) {
- int a = 10;
- try{
- System.out.println(a/0);
- int[] arr = {1,2,3,4};
- arr = null;
- System.out.println(arr[100]);
- }
- catch (ArithmeticException e){
- e.printStackTrace();
- System.out.println("出现了算术异常!");
- }
- catch(ArrayIndexOutOfBoundsException e){
- e.printStackTrace();
- System.out.println("数组越界异常!");
- }
- catch (NullPointerException e){
- e.printStackTrace();
- System.out.println("空指针异常!");
- }
- System.out.println("执行后续代码!");
- }
- }
运行结果:我们发现,捕获到算术异常以后,剩下的代码并没有被执行,这正好验证了没有同时抛出多个异常的说法!

注意:如果异常之间存在父子类关系,那么把父类放在最后!

在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。
语法:throw new XXXException("异常产生的原因");
实例:实现一个获取数组中任意位置元素的方法。
- public static int getElement(int[] array, int index){
- if(null == array){
- throw new NullPointerException("传递的数组为null");
- }
-
- if(index < 0 || index >= array.length){
- throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
- }
-
- return array[index];
- }
- public static void main(String[] args) {
- int[] array = {1,2,3};
- getElement(array, 3);
- }
运行结果:

注意:
1. throw必须写在方法体内部。
2. 抛出的对象必须是Exception 或者 Exception 的子类对象。
3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理。
4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译。
5. 异常一旦抛出,其后的代码就不会执行。
1 异常声明throws
处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常。
实例:
- public class Config {
- File file;
- // public void OpenConfig(String filename) throws IOException,FileNotFoundException{
- // FileNotFoundException 继承自 IOException
- public void OpenConfig(String filename) throws IOException{
- if(filename.endsWith(".ini")){
- throw new IOException("文件不是.ini文件");
- }
-
- if(filename.equals("config.ini")){
- throw new FileNotFoundException("配置文件名字不对");
- }
- // 打开文件
- }
- public void readConfig(){
- }
- }
结论:
1. throws必须跟在方法的参数列表之后。
2. 声明的异常必须是 Exception 或者 Exception 的子类。
3. 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出。

在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。
实例:简单输入的数据流关闭。
- public class Testdemo3 {
- public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
- try{
- int n = sc.nextInt();
- }catch (InputMismatchException e){
- e.printStackTrace();
- System.out.println("输入数据不匹配!");
- }
- finally {
- System.out.println("执行了finally部分,一般用来关闭资源!");
- sc.close();
- }
- }
- }
运行结果:我们故意输入字符串,我们发现finally部分的代码依然会被执行!
【异常处理流程总结】
🥧程序先执行 try 中的代码。
🍩如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配。
🍿如果找到匹配的异常类型, 就会执行 catch 中的代码。
🍬如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者。
🍭无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行)。
🥯如果上层调用者也没有处理的了异常, 就继续向上传递。
🥝一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止。