一、异常的概念

1.1 问题引入

  1. package com.hspedu.exception_;
  2. /**
  3. * @author HarborGao
  4. * @version 1.0
  5. */
  6. public class Exception01 {
  7. public static void main(String[] args) {
  8. int num1 = 100;
  9. int num2 = 0;
  10. //解读:
  11. //1. num1 / num2 => 10 / 0
  12. //2. 当执行到 num1 / num2 因为 num2 = 0 ,程序就会抛出异常 ArithmeticException
  13. //3. 当抛出异常后,程序就崩溃了,下面的代码就不再执行
  14. //4. 思考:这样的程序好吗?不好,不应该出现了一个不算致命的问题,就导致整个系统崩溃
  15. //5. Java设计者,提供了一个叫 异常处理机制来解决该问题
  16. int res = num1 / num2;
  17. System.out.println("程序继续运行...");
  18. }
  19. }

解决方案 - 异常捕获
如果程序员 认为一段代码可能会出现异常/问题,可以使用 try-catch异常处理机制来解决,从而保证程序的健壮性。如果进行了异常处理,即使程序出现了异常,也会继续执行。
将代码块选中 -> 快捷键 ctrl + alt + t -> 选中 try-catch

  1. try {
  2. int res = num1 / num2;
  3. } catch (Exception e) {
  4. //e.printStackTrace();
  5. System.out.println(e.getMessage()); //输出异常信息
  6. }

1.2 基本概念

Java语言中,将程序执行中发生的不正常情况称为“异常”。(注意:开发过程中的语法错误和逻辑错误不是异常)

执行过程中所发生的异常事件可分为两类:

  1. Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。
    比如:StackOverflowError【栈溢出】和OOM (out of memory),Error是严重错误,程序会崩溃。
  2. Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常【程序运行时发生的异常】和编译时异常【编程时,编译器检查出的异常】

二、异常体系图

image.png
Exception 异常 - 图2

2.1 异常体系图小结

  1. 异常分为两大类,运行时异常和编译时异常。
  2. 运行时异常,编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException 类及它的子类都是运行时异常。
  3. 对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
  4. 编译时异常,是编译器要求必须处置的异常。

三、常见的异常

3.1 NullPointException 空指针异常

当应用程序试图在需要对象的时候使用 null 时,抛出该异常
image.png

  1. public class NullPointException_ {
  2. public static void main(String[] args) {
  3. String name = null;
  4. System.out.println(name.length());
  5. }
  6. }

image.png

3.2 ArithmeticException 数学运算异常

当出现异常的运算条件时,抛出此异常。比如 整数除以零
image.png

  1. public static void main(String[] args) {
  2. int n1 = 10;
  3. int n2 = 0;
  4. System.out.println(n1 / n2);
  5. }

image.png

3.3 ArrayIndexOutOfBoundsException 数组越界异常

用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
image.png

  1. public static void main(String[] args) {
  2. int[] arr = {1,5,6};
  3. for (int i = 0; i <= arr.length; i++) {
  4. System.out.println(arr[i]);
  5. }
  6. }

image.png

3.4 ClassCastException 类型转换异常

当试图将对象强制转换为不是实例的子类时,抛出该异常。
image.png

  1. public class NullPointException_ {
  2. public static void main(String[] args) {
  3. A a = new B(); //向上转型
  4. B b = (B)a; //正确 向下转型
  5. C c = (C)a; //抛出异常
  6. }
  7. }
  8. class A {}
  9. class B extends A{}
  10. class C extends A{}

image.png

3.5 NumberFormatException 数字格式不正确异常

当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 => 使用异常我们可以确保输入是满足条件数字。
image.png

  1. public class NumberFormatException_ {
  2. public static void main(String[] args) {
  3. // String name = "123";
  4. String name = "HarborGao";
  5. //将 String 转成 int
  6. int num = Integer.parseInt(name);
  7. }
  8. }

image.png

3.6 编译异常

编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译

常见的编译异常:
SQLException:操作数据库时,查询表可能发生异常
IOException:操作文件时,发生的异常
FileNotFoundException:当操作一个不存在的文件时,发生异常
ClassNotFoundException:加载类,而该类不存在时,异常
EOFException:操作文件,到文件末尾,发生异常
IllegalArgumentException:参数异常

  1. package com.hspedu.exception_;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. /**
  5. * @author HarborGao
  6. * @version 1.0
  7. */
  8. public class Exception02 {
  9. public static void main(String[] args) {
  10. try {
  11. FileInputStream fis;
  12. fis = new FileInputStream("d:\\aa.jpg");
  13. int len;
  14. while ((len = fis.read()) != -1) {
  15. System.out.println(len);
  16. }
  17. fis.close();
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

image.png

四、异常处理

4.1 基本介绍

异常处理就是当异常发生时,对异常处理的方式

4.2 异常处理的方式

  1. try-catch-finally
    程序员在代码中捕获发生的异常,自行处理

  2. throws
    将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者是JVM

4.3 示意图

Exception 异常 - 图14
Exception 异常 - 图15

4.4 try-catch 方式处理异常说明

  1. Java提供 try 和 catch 块来处理异常。try 块用于包含可能出错的代码,catch 块用于处理try 块中发生的异常。可以根据需要在程序中有多个数量的try…catch 块。

  2. 基本语法
    try {
    //可疑代码
    //将异常生成对应的异常对象,传递给catch
    } catch(异常) {
    //对异常的处理
    }
    //如果没有finally,语法可以通过

  3. 注意事项和使用细节

    1. 如果异常发生了,则异常后面的代码不会执行,直接进入到catch 块。
    2. 如果异常没有发生,则顺序执行 try 的代码块,不会进入到 catch
    3. 如果希望 不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用 finally
    4. 可以有多个catch 语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception 在后,NullPointerException 在前),如果发生异常,只会匹配一个 catch。
    5. 可以使用 try -finally 配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉。
      应用场景,执行一段代码,不管是否发生异常,都必须执行某个业务逻辑 ```java package com.hspedu.exception.try;

/**

  • @author HarborGao
  • @version 1.0
  • 演示多个catch 语句,捕获不同的异常 */ public class TrtCatchDetail02 { public static void main(String[] args) {

    1. //1. 如果 try 代码块有可能有多个异常
    2. //2. 可以使用多个catch 分别捕获不同的异常,相应处理
    3. //3. 要求子类异常写在前面,父类异常写在后面
    4. try {
    5. Person person = new Person();
    6. person = null;
    7. System.out.println(person.getName()); //NullPointerException
    8. int n1 = 10;
    9. int n2 = 0;
    10. int res = n1 / n2; //ArithmeticException
    11. System.out.println(res);
    12. } catch (NullPointerException e) {
    13. System.out.println("空指针异常:" + e.getMessage());
    14. } catch (ArithmeticException e) {
    15. System.out.println("算术异常:" + e.getMessage());
    16. } catch (Exception e) {
    17. System.out.println(e.getMessage());
    18. }

    } } class Person { private String name;

    public String getName() {

    1. return name;

    } } ```

  1. 小练习

题1:以下代码输出什么?

  1. public class TryCatchExercise01 {
  2. public static int method() {
  3. try {
  4. String[] names = new String[3];
  5. if (names[1].equals("tom")) {
  6. System.out.println(names[1]);
  7. } else {
  8. names[3] = "hspedu";
  9. }
  10. return 1;
  11. } catch (ArrayIndexOutOfBoundsException e) {
  12. return 2;
  13. } catch (NullPointerException e) {
  14. return 3;
  15. } finally {
  16. return 4;
  17. }
  18. }
  19. public static void main(String[] args) {
  20. System.out.println(method());
  21. }
  22. }

题2:判断以下代码的输出结果。

  1. public class TryCatchExercise02 {
  2. public static int method() {
  3. int i = 1;
  4. try {
  5. i++;
  6. String[] names = new String[3];
  7. if (names[1].equals("tom")) {
  8. System.out.println(names[1]);
  9. } else {
  10. names[3] = "hspedu";
  11. }
  12. return 1;
  13. } catch (ArrayIndexOutOfBoundsException e) {
  14. return 2;
  15. } catch (NullPointerException e) {
  16. return ++i;
  17. } finally {
  18. return ++i;
  19. }
  20. }
  21. public static void main(String[] args) {
  22. System.out.println(method());
  23. }
  24. }

题3:判断以下代码的输出结果。

  1. public class TryCatchExercise03 {
  2. public static int method() {
  3. int i = 1;
  4. try {
  5. i++;
  6. String[] names = new String[3];
  7. if (names[1].equals("tom")) {
  8. System.out.println(names[1]);
  9. } else {
  10. names[3] = "hspedu";
  11. }
  12. return 1;
  13. } catch (ArrayIndexOutOfBoundsException e) {
  14. return 2;
  15. } catch (NullPointerException e) {
  16. return ++i;
  17. } finally {
  18. ++i;
  19. System.out.println("i=" + i);
  20. }
  21. }
  22. public static void main(String[] args) {
  23. System.out.println(method());
  24. }
  25. }
  1. try-catch-finally 执行顺序小结
    1. 如果没有出现异常,则执行 try 块中所有语句,不执行 catch 块中语句,如果有 finally,最后还需要执行 finally 里面的语句
    2. 如果出现异常,则 try 块中异常发生后,try 块剩下的语句不再执行。将执行 catch 块中的语句,如果有finally,最后还需要执行 finally 里面的语句!

4.5 throws 异常处理说明

  1. 基本介绍
    1. 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显式地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
    2. 在方法声明中用 throws 语句可以声明抛出异常的列表,throws 后面跟的异常类型可以是方法中产生的异常类型,也可以是它的父类。 ```java import java.io.FileInputStream; import java.io.FileNotFoundException;

public class Throws01_ { public static void main(String[] args) {

  1. }
  2. public void f1() throws FileNotFoundException,NullPointerException {
  3. //创建了一个文件流对象
  4. //老韩解读:
  5. //1. 这里的异常是一个FileNotFoundException 属于编译异常
  6. //2. 可以使用 try-catch-finally 解决
  7. //3. 也可以使用 throws 抛出异常,让调用f1方法的调用者(另一个方法)处理
  8. //4. throws 后面的异常类型可以是方法中产生的异常类型,也可以是它们的父类
  9. //5. throws 关键字后 也可以是异常列表,即可以抛出多个异常
  10. FileInputStream fis = new FileInputStream("d//aa.txt");
  11. }

}

  1. 2. 注意事项和使用细节
  2. 1. 对于编译异常,程序中必须用try-catch throws等方式处理,否则无法通过编译
  3. 1. 对于运行时异常,程序中如果没有处理,默认就是 throws 的方式处理
  4. 1. 子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常要么和父类抛出的异常一致,要么为父类所抛出异常类型的子类型
  5. 1. throws 过程中,如果有方法 try-catch,就相当于处理异常,就可以不必 throws
  6. <a name="JBiHx"></a>
  7. ### 五、自定义异常
  8. <a name="UX4f0"></a>
  9. #### 5.1 基本概念
  10. 当程序中出现了某些“错误”,但该错误信息并没有在 Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
  11. <a name="CLPvO"></a>
  12. #### 5.2 自定义异常的步骤
  13. 1. 定义类:自定义异常类名(程序员自己写)继承 Exception RuntimeException
  14. 1. 如果继承 Exception,属于编译异常
  15. 1. 如果继承 RuntimeException,属于运行异常(一般来说,继承RuntimeException
  16. ```java
  17. package com.hspedu.exception_.customexception_;
  18. /**
  19. * @author HarborGao
  20. * @version 1.0
  21. */
  22. public class CustomException {
  23. public static void main(String[] args) {
  24. int age = 300;
  25. //要求范围在 18 - 120 之间,否则抛出一个自定义异常
  26. if (!(age >= 18 && age <=120)) {
  27. throw new AgeException("年龄需要在 18 - 120 之间");
  28. }
  29. System.out.println("你的年龄范围正确");
  30. }
  31. }
  32. //解读:
  33. //1. 一般情况下,我们自定义异常是继承 RuntimeException
  34. //2. 即把自定义异常做成运行时异常,好处是,我们可以使用默认的处理机制
  35. // 即比较方便
  36. class AgeException extends RuntimeException /* Exception */ {
  37. public AgeException(String message) {
  38. super(message);
  39. }
  40. }

六、throw 和 throws 的对比

意义 位置 后面跟的东西
throws 异常处理的一种方式 方法声明处 异常类型
throw 手动生成异常对象的关键字 方法体中 异常对象

小练习

  1. 下面的测试输出什么? ```java public class ThrowException { public static void main(String[] args) {
    1. try {
    2. ReturnExceptionDemo.methodA();
    3. } catch (Exception e) {
    4. System.out.println(e.getMessage());
    5. }
    6. ReturnExceptionDemo.methodB();
    } }

class ReturnExceptionDemo { static void methodA() { try { System.out.println(“进入方法A”); throw new RuntimeException(“制造异常”); } finally { System.out.println(“用A方法的finally”); } }

  1. static void methodB() {
  2. try {
  3. System.out.println(" 进入方法B");
  4. return;
  5. } finally {
  6. System.out.println("调用B方法的finally");
  7. }
  8. }

}

  1. <a name="eFL8V"></a>
  2. ### 七、本章作业
  3. 1. 编程题
  4. 1. 编写应用程序 EcmDef.java,接收命令行的两个参数(整数),计算两数相除。
  5. 1. 计算两个数相除,要求使用方法 cal(int n1, int n2)
  6. 1. 对数据格式不正确、缺少命令行参数、除0 进行异常处理
  7. 2. 说出以下代码是否会发生异常,如果会,是哪种异常?如果不会,则打印结果是什么?
  8. ```java
  9. public class Homework02 {
  10. public static void main(String[] args) {
  11. if (args[4].equals("john")) { //可能会发生数组越界异常
  12. System.out.println("AA");
  13. } else {
  14. System.out.println("BB");
  15. }
  16. Object o = args[2];
  17. Integer i = (Integer) o; //类型转换异常
  18. }
  19. }
  1. 写出程序结果

    1. public class Homework03 {
    2. public static void func() {
    3. try{
    4. throw new RuntimeException();
    5. } finally {
    6. System.out.println("B");
    7. }
    8. }
    9. public static void main(String[] args) {
    10. try {
    11. func();
    12. System.out.println("A");
    13. } catch (Exception e) {
    14. System.out.println("C");
    15. }
    16. System.out.println("D");
    17. }
    18. }
  2. 写出程序结果

    1. public class Homework04 {
    2. public static void main(String[] args) {
    3. try {
    4. showExce();
    5. System.out.println("A");
    6. } catch (Exception e) {
    7. System.out.println("B");
    8. } finally {
    9. System.out.println("C");
    10. }
    11. System.out.println("D");
    12. }
    13. public static void showExce() throws Exception {
    14. throw new Exception();
    15. }
    16. }

学习参考(致谢):

  1. B站 @程序员鱼皮 Java学习一条龙
  2. B站 @韩顺平 零基础30天学会Java