💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
>[success] # java 异常 1. 在Java语言中主要指程序执行中发生的不正常情况,通过`java.lang.Throwable`类是Java语言中错误(Error)和异常(Exception)的超类 2. `Error`类主要用于描述**Java虚拟机无法解决的严重错误**,通常无法编码解决,如:JVM挂掉了 3. `Exception`类主要用于描述因编程**错误或偶然外在因素导致的轻微错误**,通常可以编码解决,如:0作为除数,`java.lang.Exception`类是所有异常的超类,主要分两种**非检测性异常**,**叫作检测性异常** * `RuntimeException` - 运行时异常,也叫作非检测性异常(`ArithmeticException`类 - 算术异常/`ArrayIndexOutOfBoundsException`类 - 数组下标越界异常/`NullPointerException` - 空指针异常/`ClassCastException` - 类型转换异常/`NumberFormatException `- 数字格式异常) ~~~ public class ExceptionTest { public static void main(String[] args) { int i = 5; int j = 0; // 非检测性异常 运行时异常 System.out.println(i / j); // 编译ok,运行阶段会发生算术异常 下面的代码不会执行 System.out.println("上面异常我不能打印"); } } ~~~ **执行报错信息**,当程序执行过程中发生异常但又没有手动处理时,则由Java虚拟机采用默认方式处理异常,而默认 处理方式就是:打印异常的名称、异常发生的原因、异常发生的位置以及终止程序 ![](https://img.kancloud.cn/10/54/10549e14b18d05455a6abf1919c2e35b_666x35.png) * `IOException`和`其它异常` ,也叫作检测性异常,所谓检测性异常就是指在编译阶段都能被编译器检测出来的异常 ![](https://img.kancloud.cn/7d/bc/7dbcfdcd6c93cf73e1dcf58d46f412c1_1081x479.png) >[danger] ##### 解决异常 1. 解决异常就要增加代码的强壮性。可以通过**if条件**判断来避免异常的发生,但带来的问题过多的if条件判断会导致程序的代码**加长、臃肿,可读性差**,下面代码通过if 判断杜绝了下面会产生的异常 ~~~ import java.io.IOException; public class ExceptionPreventTest { public static void main(String[] args) { // 会发生算术异常 int ia = 10; int ib = 0; if (0 != ib) { System.out.println(ia / ib); } // 数组下标越界异常 int[] arr = new int[5]; int pos = 5; if (pos >= 0 && pos < 5) { System.out.println(arr[pos]); } // 发生空指针异常 String str = null; if (null != str) { System.out.println(str.length()); } // 类型转换异常 Exception ex = new Exception(); if (ex instanceof IOException) { IOException ie = (IOException) ex; } // 数字格式异常 String str2 = "123a"; if (str2.matches("\\d+")) { System.out.println(Integer.parseInt(str2)); } System.out.println("程序总算正常结束了!"); } } ~~~ >[danger] ##### 采用异常的捕获 * 语法格式 ~~~ try { 编写可能发生异常的代码; } catch(异常类型 引用变量名) { 编写针对该类异常的处理代码; } ... finally { 编写无论是否发生异常都要执行的代码; } ~~~ 需要注意点: 1. 当需要编写多个catch分支时,切记小类型应该放在大类型的前面 2. `catch(Exception e) {}` 作为所有异常的超类 可以捕获所有情况 ~~~ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class ExceptionTest { public static void main(String[] args) { // 创建一个FileInputStream类型的对象与d:/a.txt文件关联,打开文件 FileInputStream fis = null; try { System.out.println("1"); // 当程序执行过程中发生了异常后直奔catch分支进行处理 fis = new FileInputStream("d:/a.txt"); System.out.println("2"); } catch (FileNotFoundException e) { System.out.println("3"); e.printStackTrace(); // 打印报错堆栈信息错误 System.out.println("4"); } // 层层捕获 try { System.out.println("5"); fis.close(); System.out.println("6"); } catch (IOException e) { System.out.println("7"); e.printStackTrace(); System.out.println("8"); } catch (NullPointerException e) { e.printStackTrace(); } // 一次性捕获 try { System.out.println("5"); fis.close(); System.out.println("6"); } catch (Exception e) { e.printStackTrace(); } // 不能将大捕获写在小捕获前面 // try { // System.out.println("5"); // fis.close(); // System.out.println("6"); // } /*catch (Exception e) { // e.printStackTrace(); // }*/ catch (IOException e) { // System.out.println("7"); // e.printStackTrace(); // System.out.println("8"); // } catch (NullPointerException e) { // e.printStackTrace(); // } catch (Exception e) { // e.printStackTrace(); // } } } ~~~ * `finally` 表示这段语句最终一定会被执行(不管有没有抛出异常) ~~~ try { a; b; - 可能发生异常的语句 c; }catch(Exception ex) { d; }finally { e; } ~~~ 当**没有发生异常**时的执行流程:**a b c e**;**当发生异常**时的执行流程:**a b d e** ~~~ public class ExceptionTest { // 笔试考点 public static int test() { try { int[] arr = new int[5]; System.out.println(arr[5]); return 0; } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); return 1; } finally { return 2; // 提交结束方法并返回数据 } } public static void main(String[] args) { try { int ia = 10; int ib = 0; System.out.println(ia / ib); } catch (ArithmeticException e) { e.printStackTrace(); String str1 = null; // str1.length(); // 会发生空指针异常 } finally { System.out.println("无论是否发生异常都记得来执行我哦!"); // 依然是执行 } // 如果执行了 (str1.length(); // 会发生空指针异常 )往下不执行了 System.out.println("Over!"); System.out.println("----------------------------------------"); int test = test(); System.out.println("test = " + test); // 2 } } ~~~ >[info] ## throw 抛出异常 1. 在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移给该方法的调用者,这种方法就叫异常的抛出。当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代码后续的代码就不再执行 2. 写法`访问权限 返回值类型 方法名称(形参列表) throws 异常类型1,异常类型2,...{ 方法体; }` 例如 `public void show() throws IOException{}` >[danger] ##### 正常使用try...catch 去捕获异常 1. 下面案例在 被调用函数内部使用 `try...catch` 捕获,当在`show1` 和 `main` 函数调用 show 函数时候并不影响往下执行 ~~~ import java.io.FileInputStream; import java.io.IOException; public class ThrowTest { // 定义一个异常方法 public static void show() { try (FileInputStream fis = new FileInputStream("D:/vscodeJava/JavaBaseTest/src/ThrowTest1.java")) { fis.close(); } catch (IOException e) { System.out.println("进入异常"); // TODO Auto-generated catch block e.printStackTrace(); } } public static void show2() { try { show(); } catch (Exception e) { System.out.println("被我捕获了么"); e.printStackTrace(); } } public static void main(String[] args) { show2(); // 异常但下面执行了 System.out.println("是否执行"); } } ~~~ * 打印结果 ![](https://img.kancloud.cn/62/28/622861095f8b3d559e1013f0c36240b0_1108x251.png) >[danger] ##### 在使用函数地方能过获取异常 1. 当想让最后使用的地方获取异常来决定最后对异常的处理可以使用抛出异常形式 ~~~ import java.io.FileInputStream; import java.io.IOException; public class ThrowTest { // 定义一个异常方法 public static void show() throws IOException { FileInputStream fis = new FileInputStream("D:/vscodeJava/JavaBaseTest/src/ThrowTest1.java"); fis.close(); } public static void show2() throws IOException { show(); } // 不推荐 不建议在main方法中抛出异常 JVM负担很重 // public static void main(String[] args) throws IOException{ // show2(); // 异常但下面执行了 // System.out.println("是否执行"); // } public static void main(String[] args) { show2(); // 异常 System.out.println("停止执行"); // 因为异常这类不执行了 } // 通过捕获执行了 // public static void main(String[] args) { // try { // show2(); // } catch (Exception e) { // System.out.println("获取异常"); // } // // 异常但下面执行了 // System.out.println("是否执行"); // } } ~~~ >[danger] ##### 子类重写异常 1. 子类重写的方法**不能抛出更大的异常、不能抛出平级不一样的异常**,**但可以抛出一样的异常、更小的异常以及不抛出异常** >[info] ## 使用捕获还是抛出异常 1. 若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理。 2. 若一个方法内部又以递进方式分别调用了好几个其它方法,则建议这些方法内可以使用抛出的方法处理到最后一层进行捕获方式处理。 >[info] ## 自定义异常 1. 除了java 官方提供的异常,也可以自定义异常类 * 自定义xxxException异常类**继承Exception类或者其子类** * 提供两个版本的构造方法,一个是**无参构造方法**,另外一个是**字符串作为参数的构造方法** ~~~ public class AgeException extends Exception { static final long serialVersionUID = 7818375828146090155L; // 序列化的版本号 与序列化操作有关系 public AgeException() { } public AgeException(String message) { super(message); } } ~~~ * 使用 ~~~ public class Person { private String name; private int age; public Person() { } public Person(String name, int age) /*throws AgeException */{ setName(name); setAge(age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) /*throws AgeException */{ if (age > 0 && age < 150) { this.age = age; } else { //System.out.println("年龄不合理哦!!!"); try { throw new AgeException("年龄不合理哦!!!"); } catch (AgeException e) { e.printStackTrace(); } } } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } ~~~