Przeglądaj źródła

docs: 更新20260728课堂笔记,同步代码变更——新增异常常用方法(getMessage/fillInStackTrace/getCause)、自定义异常(AgeOutOfBoundsException)、throw与throws关键字(Person+Demo03)、完善对比表格与练习要点

WanJL 6 godzin temu
rodzic
commit
2c3871b386

+ 2 - 2
README.md

@@ -33,7 +33,7 @@
 | 2026-07-23 | [抽象类 + final + 模板方法 + 代码块 + 接口(含JDK8+新特性):抽象类定义与特点、final 类/方法/变量、模板方法模式、三种代码块、接口定义与实现(Usb→LogiMouse/ROGMouse)、接口 vs 抽象类对比、JDK8默认方法static方法、JDK9私有方法](./课堂笔记/01-JavaSE基础-笔记/20260723-笔记.md) |
 | 2026-07-23 | [抽象类 + final + 模板方法 + 代码块 + 接口(含JDK8+新特性):抽象类定义与特点、final 类/方法/变量、模板方法模式、三种代码块、接口定义与实现(Usb→LogiMouse/ROGMouse)、接口 vs 抽象类对比、JDK8默认方法static方法、JDK9私有方法](./课堂笔记/01-JavaSE基础-笔记/20260723-笔记.md) |
 | 2026-07-24 | [多态 + 多态中的转型 + 内部类:多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof类型判断、多态的好处(开闭原则)、抽象类与多态结合(Animal→Dog;Person→Student/Teacher综合案例)、Usb接口多态案例(NoteBook接入鼠标/猫)、内部类概念与使用场景、成员内部类、私有内部类设计原则](./课堂笔记/01-JavaSE基础-笔记/20260724-笔记.md) |
 | 2026-07-24 | [多态 + 多态中的转型 + 内部类:多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof类型判断、多态的好处(开闭原则)、抽象类与多态结合(Animal→Dog;Person→Student/Teacher综合案例)、Usb接口多态案例(NoteBook接入鼠标/猫)、内部类概念与使用场景、成员内部类、私有内部类设计原则](./课堂笔记/01-JavaSE基础-笔记/20260724-笔记.md) |
 | 2026-07-27 | [匿名内部类 + Java API 与 String 类 + Math + 日期时间:内部类三大分类、匿名内部类四种使用方式、综合案例(USB/餐具/学生排序/计算器/动物叫声);API概念、String内存模型/不可变性/可变字符串StringBuffer&StringBuilder/文本块TextBlock JDK15+/String常用方法大全;Math数学类常用方法;日期时间类(Date/LocalDate/LocalTime/LocalDateTime/DateTimeFormatter)](./课堂笔记/01-JavaSE基础-笔记/20260727-笔记.md) |
 | 2026-07-27 | [匿名内部类 + Java API 与 String 类 + Math + 日期时间:内部类三大分类、匿名内部类四种使用方式、综合案例(USB/餐具/学生排序/计算器/动物叫声);API概念、String内存模型/不可变性/可变字符串StringBuffer&StringBuilder/文本块TextBlock JDK15+/String常用方法大全;Math数学类常用方法;日期时间类(Date/LocalDate/LocalTime/LocalDateTime/DateTimeFormatter)](./课堂笔记/01-JavaSE基础-笔记/20260727-笔记.md) |
-| 2026-07-28 | [Java 异常处理:异常概念、Throwable 体系结构(Exception vs Error)、编译期异常 vs 运行时异常、JVM 默认处理、try-catch 多 catch 块、throws 抛出、鲁棒性;课后作业回顾 — String 分割去重/统计、LocalDate 日期天数差计算](./课堂笔记/01-JavaSE基础-笔记/20260728-笔记.md) |
+| 2026-07-28 | [Java 异常处理:异常概念、Throwable 体系结构(Exception vs Error)、编译期异常 vs 运行时异常、JVM 默认处理、try-catch 多 catch 块、异常常用方法(getMessage/fillInStackTrace/getCause)、throws 抛出、鲁棒性;自定义异常(继承 Exception/继承 RuntimeException 区别);throw 手动抛出 vs throws 声明异常;课后作业回顾 — String 分割去重/统计、LocalDate 日期天数差计算](./课堂笔记/01-JavaSE基础-笔记/20260728-笔记.md) |
 
 
 ## 二、授课代码
 ## 二、授课代码
 
 
@@ -53,7 +53,7 @@
 | 2026-07-23 | 抽象类 + final + 模板方法 + 代码块 + 接口(含JDK8+新特性):抽象类定义与特点、final 类/方法/变量、模板方法模式(MyOneDayTemplate)、三种代码块(局部/构造/静态)、接口定义与实现(Usb→LogiMouse/ROGMouse)、接口 vs 抽象类对比、JDK8默认方法/静态方法(MyDefaultInterface→MyDefaultInterfaceImpl)、JDK9私有方法(抽取多个默认方法公共代码)、Test05 测试类 | `c260723` | [`course`](./授课代码/c260723/src/course/) |
 | 2026-07-23 | 抽象类 + final + 模板方法 + 代码块 + 接口(含JDK8+新特性):抽象类定义与特点、final 类/方法/变量、模板方法模式(MyOneDayTemplate)、三种代码块(局部/构造/静态)、接口定义与实现(Usb→LogiMouse/ROGMouse)、接口 vs 抽象类对比、JDK8默认方法/静态方法(MyDefaultInterface→MyDefaultInterfaceImpl)、JDK9私有方法(抽取多个默认方法公共代码)、Test05 测试类 | `c260723` | [`course`](./授课代码/c260723/src/course/) |
 | 2026-07-24 | 多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof运算符、多态的好处(开闭原则)、抽象类+多态(Animal→Dog、Person→Student/Teacher→Test03.runPerson())、接口多态(Usb→Mouse/Cat→NoteBook)、内部类概念与使用场景 | `c260724` | [`course/polymorphism`](./授课代码/c260724/src/course/polymorphism/) / [`course/internalClass`](./授课代码/c260724/src/course/internalClass/) |
 | 2026-07-24 | 多态概念与前提、多态成员访问特点、向上转型/向下转型、instanceof运算符、多态的好处(开闭原则)、抽象类+多态(Animal→Dog、Person→Student/Teacher→Test03.runPerson())、接口多态(Usb→Mouse/Cat→NoteBook)、内部类概念与使用场景 | `c260724` | [`course/polymorphism`](./授课代码/c260724/src/course/polymorphism/) / [`course/internalClass`](./授课代码/c260724/src/course/internalClass/) |
 | 2026-07-27 | 匿名内部类:内部类三大分类概念回顾、匿名内部类语法格式与本质、三种使用方式(多态接收变量赋值 / 方法参数传递 / 匿名对象直接调用)、配套接口/抽象类定义(Usb / Tableware / Compute / Sort)、综合案例(NoteBook USB设备 / Person 餐具 / 学生排序比较 / Calculator计算器 / Animal动物叫声);Java API:API概念、String类(内存模型/构造方法/==与equals/常用方法大全/不可变性)、可变字符串(StringBuffer/SringBuilder)、文本块TextBlock JDK15+;Math数学类;日期时间类(Date/LocalDate/LocalTime/LocalDateTime/DateTimeFormatter) | `c260727` | [`course/anonymousInternalClass`](./授课代码/c260727/src/course/anonymousInternalClass/) / [`course/api`](./授课代码/c260727/src/course/api/) / [`course/api/string`](./授课代码/c260727/src/course/api/string/) / [`course/api/math`](./授课代码/c260727/src/course/api/math/) / [`course/api/date`](./授课代码/c260727/src/course/api/date/) / [`exercise/exercise01`](./授课代码/c260727/src/exercise/exercise01/) / [`exercise/exercise06`](./授课代码/c260727/src/exercise/exercise06/) / [`exercise/exercise07`](./授课代码/c260727/src/exercise/exercise07/) / [`homework_0724/p2_myarray`](./授课代码/c260727/src/homework_0724/p2_myarray/) |
 | 2026-07-27 | 匿名内部类:内部类三大分类概念回顾、匿名内部类语法格式与本质、三种使用方式(多态接收变量赋值 / 方法参数传递 / 匿名对象直接调用)、配套接口/抽象类定义(Usb / Tableware / Compute / Sort)、综合案例(NoteBook USB设备 / Person 餐具 / 学生排序比较 / Calculator计算器 / Animal动物叫声);Java API:API概念、String类(内存模型/构造方法/==与equals/常用方法大全/不可变性)、可变字符串(StringBuffer/SringBuilder)、文本块TextBlock JDK15+;Math数学类;日期时间类(Date/LocalDate/LocalTime/LocalDateTime/DateTimeFormatter) | `c260727` | [`course/anonymousInternalClass`](./授课代码/c260727/src/course/anonymousInternalClass/) / [`course/api`](./授课代码/c260727/src/course/api/) / [`course/api/string`](./授课代码/c260727/src/course/api/string/) / [`course/api/math`](./授课代码/c260727/src/course/api/math/) / [`course/api/date`](./授课代码/c260727/src/course/api/date/) / [`exercise/exercise01`](./授课代码/c260727/src/exercise/exercise01/) / [`exercise/exercise06`](./授课代码/c260727/src/exercise/exercise06/) / [`exercise/exercise07`](./授课代码/c260727/src/exercise/exercise07/) / [`homework_0724/p2_myarray`](./授课代码/c260727/src/homework_0724/p2_myarray/) |
-| 2026-07-28 | Java 异常处理(Throwable体系结构、try-catch多catch块、throws抛出、鲁棒性) + 课后作业回顾(String分割去重统计 + LocalDate天数差计算) | `c260728` | [`course`](./授课代码/c260728/src/course/) / [`homework260727`](./授课代码/c260728/src/homework260727/) |
+| 2026-07-28 | Java 异常处理(Demo01异常常用方法 + Demo02自定义异常 + Demo03 throw/throws + Person+AgeOutOfBoundsException) + 课后作业回顾(String工具 + 日期数学计算) | `c260728` | [`course`](./授课代码/c260728/src/course/) / [`homework260727`](./授课代码/c260728/src/homework260727/) |
 
 
 ## 三、课堂练习题
 ## 三、课堂练习题
 
 

+ 86 - 0
授课代码/c260728/src/course/AgeOutOfBoundsException.java

@@ -0,0 +1,86 @@
+package course;
+
+import java.security.PrivilegedActionException;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title AgeOutOfBoundsException
+ * @description 年龄越界异常类
+ * @create 2026/7/28
+ */
+public class AgeOutOfBoundsException extends Exception{
+    /**
+     * Constructs a new exception with {@code null} as its detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     */
+    public AgeOutOfBoundsException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.  The
+     * cause is not initialized, and may subsequently be initialized by
+     * a call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public AgeOutOfBoundsException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *                by the {@link #getMessage()} method).
+     * @param cause   the cause (which is saved for later retrieval by the
+     *                {@link #getCause()} method).  (A {@code null} value is
+     *                permitted, and indicates that the cause is nonexistent or
+     *                unknown.)
+     * @since 1.4
+     */
+    public AgeOutOfBoundsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified cause and a detail
+     * message of {@code (cause==null ? null : cause.toString())} (which
+     * typically contains the class and detail message of {@code cause}).
+     * This constructor is useful for exceptions that are little more than
+     * wrappers for other throwables (for example, {@link
+     * PrivilegedActionException}).
+     *
+     * @param cause the cause (which is saved for later retrieval by the
+     *              {@link #getCause()} method).  (A {@code null} value is
+     *              permitted, and indicates that the cause is nonexistent or
+     *              unknown.)
+     * @since 1.4
+     */
+    public AgeOutOfBoundsException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message,
+     * cause, suppression enabled or disabled, and writable stack
+     * trace enabled or disabled.
+     *
+     * @param message            the detail message.
+     * @param cause              the cause.  (A {@code null} value is permitted,
+     *                           and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression  whether or not suppression is enabled
+     *                           or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     * @since 1.7
+     */
+    public AgeOutOfBoundsException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}

+ 10 - 3
授课代码/c260728/src/course/Demo01.java

@@ -47,7 +47,7 @@ public class Demo01 {
                 2、程序停止运行
                 2、程序停止运行
 
 
             我们处理异常的方式也分为两种:
             我们处理异常的方式也分为两种:
-                我们自己捕获异常,并且处理异常。
+                1、我们自己捕获异常,并且处理异常。
                    格式:
                    格式:
                         try{
                         try{
                             可能出现异常的代码
                             可能出现异常的代码
@@ -66,23 +66,30 @@ public class Demo01 {
                     程序出现异常但能够处理或能够正常运行,体现的是程序的 鲁棒性(健壮性)
                     程序出现异常但能够处理或能够正常运行,体现的是程序的 鲁棒性(健壮性)
                     兜住异常,给开发人员发邮件
                     兜住异常,给开发人员发邮件
 
 
-                我们不处理异常,交给调用者去处理,比如写了一个方法,里面会有编译期异常,我们不处理,只抛出。
+                2、我们不处理异常,交给调用者去处理,比如写了一个方法,里面会有编译期异常,我们不处理,只抛出。
                 谁调用了这个方法,谁去处理,他如果不处理也继续抛出,直到最终抛给JVM,JVM进行默认的处理。
                 谁调用了这个方法,谁去处理,他如果不处理也继续抛出,直到最终抛给JVM,JVM进行默认的处理。
 
 
      */
      */
     public static void main(String[] args) {
     public static void main(String[] args) {
         int a=0;
         int a=0;
+        int[]arr;
         try {   //这里面是可能出现异常的情况
         try {   //这里面是可能出现异常的情况
             //a = 10 / 0;
             //a = 10 / 0;
-            int[] arr={};
+            arr= new int[]{};
             arr[3]=5;
             arr[3]=5;
+            System.out.println("55555555555555555");
         }catch (ArithmeticException e){
         }catch (ArithmeticException e){
             System.out.println("不能除以0");
             System.out.println("不能除以0");
             System.out.println("以及一些其他的异常处理操作.....");
             System.out.println("以及一些其他的异常处理操作.....");
+            System.out.println(e.getMessage());
         }catch (RuntimeException e){
         }catch (RuntimeException e){
             System.out.println("出现了运行时异常");
             System.out.println("出现了运行时异常");
+            System.out.println("输出的异常信息:"+e.getMessage());
+            System.out.println("输出完整的异常信息:"+e.fillInStackTrace());
+            System.out.println(e.getCause());
         }catch (Exception e){
         }catch (Exception e){
             System.out.println("出现了异常..但不是运行时异常");
             System.out.println("出现了异常..但不是运行时异常");
+            System.out.println(e.getMessage());
         }
         }
         System.out.println(a);  //这种,代码写的时候不报错,但是运行后报错的,就是运行时异常
         System.out.println(a);  //这种,代码写的时候不报错,但是运行后报错的,就是运行时异常
         System.out.println("........后续的代码正在运行................");
         System.out.println("........后续的代码正在运行................");

+ 27 - 0
授课代码/c260728/src/course/Demo02.java

@@ -0,0 +1,27 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo02
+ * @description 自定义异常
+ * @create 2026/7/28
+ */
+public class Demo02 {
+    /*
+         当Java提供的异常不能满足我们的需求时,我们就可以自定义异常
+         实现步骤:
+            1、定义(创建)异常类
+            2、编写继承关系(可以继承Exception,那这个类就是编译期异常类。也可以继承RuntimeException,那就是运行期异常)
+            3、提供无参构造方法
+            4、提供有参构造方法
+
+           比如 判断年龄的  年龄异常
+           AgeOutOfBoundsException
+
+
+     */
+
+
+
+}

+ 44 - 0
授课代码/c260728/src/course/Demo03.java

@@ -0,0 +1,44 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo03
+ * @description throw 和 throws
+ * @create 2026/7/28
+ */
+public class Demo03 {
+    /*
+    throw
+        格式:
+            throw new 异常类();
+            throw 是要在方法体内部的,跟着的是异常对象名
+            表示手动抛出异常对象,有方法体里面的语句进行处理
+         比如:
+            public void setAge(int age) {
+                if (age<0||age>130){
+                    //要报异常
+                    throw new AgeOutOfBoundsException();
+                }
+                this.age = age;
+            }
+
+       throws
+            是用在方法的声明后面,后面跟着的是异常名。
+            表示声明异常,调用这个方法可能会出现这样的异常。
+     */
+    public static void main(String[] args) throws AgeOutOfBoundsException {
+        Person p=new Person();
+        p.setName("张三");
+        p.setAge(200);
+        System.out.println("姓名:"+p.getName());
+        System.out.println("年龄:"+p.getAge());
+        System.out.println("......................");
+        //调用setAge(20); 会出现提示,该方法可能有异常,需要处理:
+        // 我们可以选择自己使用try-catch处理
+        //也可以选择我们不处理,继续往上抛,最终抛给JVM处理
+    }
+
+
+
+}

+ 51 - 0
授课代码/c260728/src/course/Person.java

@@ -0,0 +1,51 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Person
+ * @description
+ * @create 2026/7/28
+ */
+public class Person {
+    private String name;
+    private int age;
+
+
+    public Person() {
+    }
+
+    public Person(String name, int age) {
+        this.name = name;
+        this.age = age;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+    /*
+        if (age<0||age>130){
+            //要报异常
+            throw new AgeOutOfBoundsException();
+        }
+        表示 如果if语句为true,那么就可以出现这个异常。
+
+         throws AgeOutOfBoundsException
+         表示,谁调用这个方法,可能出现这个异常
+     */
+    public void setAge(int age) throws AgeOutOfBoundsException{
+        if (age<0||age>130){
+            //要报异常,也就是要创建异常对象
+            throw new AgeOutOfBoundsException("年龄越界了,最小是1岁,最大是130岁");
+        }
+        this.age = age;
+    }
+}

+ 220 - 23
课堂笔记/01-JavaSE基础-笔记/20260728-笔记.md

@@ -89,30 +89,58 @@ try {
 - 一旦匹配到某个异常类型,就执行对应的 catch 块,然后跳出 try-catch 结构
 - 一旦匹配到某个异常类型,就执行对应的 catch 块,然后跳出 try-catch 结构
 - 建议将 **子类异常放在前面,父类异常放在后面**,否则父类异常会"拦住"子类异常,导致子类 catch 块不可达
 - 建议将 **子类异常放在前面,父类异常放在后面**,否则父类异常会"拦住"子类异常,导致子类 catch 块不可达
 
 
-#### 代码示例
+#### 代码示例(更新版——含异常常用方法)
 
 
 ```java
 ```java
 int a = 0;
 int a = 0;
+int[] arr;
 try {
 try {
-    int[] arr = {};
+    arr = new int[]{};
     arr[3] = 5;  // 数组下标越界 → ArrayIndexOutOfBoundsException
     arr[3] = 5;  // 数组下标越界 → ArrayIndexOutOfBoundsException
+    System.out.println("55555555555555555");  // 不会执行
 } catch (ArithmeticException e) {
 } catch (ArithmeticException e) {
     System.out.println("不能除以0");
     System.out.println("不能除以0");
     System.out.println("以及一些其他的异常处理操作.....");
     System.out.println("以及一些其他的异常处理操作.....");
+    System.out.println(e.getMessage());       // 获取异常描述信息
 } catch (RuntimeException e) {
 } catch (RuntimeException e) {
     System.out.println("出现了运行时异常");
     System.out.println("出现了运行时异常");
+    System.out.println("输出的异常信息:" + e.getMessage());           // 异常描述
+    System.out.println("输出完整的异常信息:" + e.fillInStackTrace()); // 完整堆栈追踪
+    System.out.println(e.getCause());                                 // 异常原因(若无返回null)
 } catch (Exception e) {
 } catch (Exception e) {
     System.out.println("出现了异常..但不是运行时异常");
     System.out.println("出现了异常..但不是运行时异常");
+    System.out.println(e.getMessage());
 }
 }
 System.out.println(a);
 System.out.println(a);
 System.out.println("........后续的代码正在运行................");
 System.out.println("........后续的代码正在运行................");
 ```
 ```
 
 
-> 📄 来源:`Demo01.java`
+> 📄 来源:`Demo01.java`(更新后)
 
 
 **输出结果分析:** 由于 `arr[3] = 5` 抛出的是 `ArrayIndexOutOfBoundsException`,它是 `RuntimeException` 的子类,因此会被第二个 catch 块捕获,输出"出现了运行时异常",然后程序继续执行后续代码。
 **输出结果分析:** 由于 `arr[3] = 5` 抛出的是 `ArrayIndexOutOfBoundsException`,它是 `RuntimeException` 的子类,因此会被第二个 catch 块捕获,输出"出现了运行时异常",然后程序继续执行后续代码。
 
 
-### 1.5 异常处理方式二:throws 抛出
+### 1.5 异常常用方法(Throwable 常用 API)
+
+当 catch 捕获到异常对象 `e` 后,可以调用 `Throwable` 类提供的方法来获取异常的详细信息:
+
+| 方法 | 说明 |
+|------|------|
+| `String getMessage()` | 返回异常的详细描述信息(最常用) |
+| `Throwable fillInStackTrace()` | 返回完整的堆栈追踪信息 |
+| `Throwable getCause()` | 返回异常的原因(若无 cause 则返回 null) |
+| `void printStackTrace()` | 将异常的堆栈追踪信息输出到标准错误流 |
+
+```java
+catch (RuntimeException e) {
+    System.out.println(e.getMessage());              // 如:"Index 3 out of bounds for length 0"
+    System.out.println(e.fillInStackTrace());        // 完整堆栈信息
+    System.out.println(e.getCause());                // 异常原因(通常为 null)
+}
+```
+
+> 📄 来源:`Demo01.java`(catch 块中的新增调用)
+
+### 1.6 异常处理方式二:throws 抛出
 
 
 我们不处理异常,交给调用者去处理。如果方法内包含编译期异常,可以不在方法内处理,只使用 `throws` 抛出:
 我们不处理异常,交给调用者去处理。如果方法内包含编译期异常,可以不在方法内处理,只使用 `throws` 抛出:
 - 谁调用了这个方法,谁去处理
 - 谁调用了这个方法,谁去处理
@@ -127,7 +155,7 @@ public void method() throws IOException {
 
 
 > 📄 来源:`Demo01.java`(概念说明)
 > 📄 来源:`Demo01.java`(概念说明)
 
 
-### 1.6 异常处理的编程思想 — 鲁棒性(健壮性)
+### 1.7 异常处理的编程思想 — 鲁棒性(健壮性)
 
 
 - **处理异常**:能处理就处理,处理不了的也要让程序能够继续运行,这才是最重要的
 - **处理异常**:能处理就处理,处理不了的也要让程序能够继续运行,这才是最重要的
 - **鲁棒性(Robustness)**:程序出现异常但能够处理或能够正常运行,体现程序的健壮性
 - **鲁棒性(Robustness)**:程序出现异常但能够处理或能够正常运行,体现程序的健壮性
@@ -137,16 +165,180 @@ public void method() throws IOException {
 
 
 ---
 ---
 
 
-## 二、课后作业回顾 — StringUtilTest 字符串工具测试
+## 二、自定义异常(Custom Exception)
+
+### 2.1 为什么需要自定义异常
+
+当 Java 官方提供的异常类不能满足我们的业务需求时,可以**自定义异常类**。例如,在年龄校验场景中,需要一个 `AgeOutOfBoundsException` 来表示年龄越界的业务异常。
+
+### 2.2 自定义异常的实现步骤
+
+| 步骤 | 说明 |
+|------|------|
+| 1 | 定义(创建)异常类 |
+| 2 | 编写继承关系:继承 `Exception`(编译期异常)或继承 `RuntimeException`(运行时异常) |
+| 3 | 提供 **无参构造方法** |
+| 4 | 提供 **有参构造方法**(带 String message 参数) |
+
+> 📄 来源:`Demo02.java`
+
+### 2.3 自定义异常代码示例
+
+```java
+// 自定义异常类 — 继承 Exception(编译期异常)
+public class AgeOutOfBoundsException extends Exception {
+
+    // 无参构造方法
+    public AgeOutOfBoundsException() {
+    }
+
+    // 有参构造方法 — 传入异常描述信息
+    public AgeOutOfBoundsException(String message) {
+        super(message);
+    }
+
+    // 带 cause 的构造方法
+    public AgeOutOfBoundsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    // 仅带 cause 的构造方法
+    public AgeOutOfBoundsException(Throwable cause) {
+        super(cause);
+    }
+}
+```
+
+> 📄 来源:`AgeOutOfBoundsException.java`
+
+**注意事项:**
+- 继承 `Exception` → 该类为 **编译期异常(Checked Exception)**,调用者必须处理(try-catch 或 throws)
+- 继承 `RuntimeException` → 该类为 **运行时异常(Unchecked Exception)**,编译器不强制处理
+
+---
+
+## 三、throw 和 throws 关键字
+
+### 3.1 throw — 手动抛出异常
+
+`throw` 关键字用于在 **方法体内部** 手动抛出一个异常对象。
+
+#### 语法格式
+
+```java
+throw new 异常类名();
+throw new 异常类名("异常描述信息");
+```
+
+#### 使用场景
+
+当方法内部检测到某种业务条件不满足时,手动创建异常对象并抛出。
+
+#### 代码示例(Person 类的 setAge 方法)
+
+```java
+public class Person {
+    private String name;
+    private int age;
+
+    // setAge 方法 — 使用 throw 手动抛出异常
+    public void setAge(int age) throws AgeOutOfBoundsException {
+        if (age < 0 || age > 130) {
+            // 手动创建异常对象并抛出
+            throw new AgeOutOfBoundsException("年龄越界了,最小是1岁,最大是130岁");
+        }
+        this.age = age;
+    }
+}
+```
+
+> 📄 来源:`Person.java`
+
+**逻辑说明:**
+- `if (age < 0 || age > 130)` 为 `true`,表示年龄不合法
+- `throw new AgeOutOfBoundsException(...)` — 手动创建异常对象并抛出,程序跳转到异常处理流程
+- 如果 `throw` 被执行,后续的 `this.age = age;` 将不会执行
+
+### 3.2 throws — 声明可能抛出的异常
+
+`throws` 关键字用在 **方法声明后面**,后面跟着异常类名,表示该方法可能会抛出某种异常,调用者需要对此做准备。
+
+#### 语法格式
+
+```java
+public void 方法名(参数列表) throws 异常类名1, 异常类名2 {
+    // 方法体
+}
+```
+
+#### 代码示例
+
+```java
+public class Demo03 {
+    public static void main(String[] args) throws AgeOutOfBoundsException {
+        Person p = new Person();
+        p.setName("张三");
+        p.setAge(200);  // 因为 setAge 声明了 throws AgeOutOfBoundsException
+                         // 所以编译期会提示需要处理(try-catch 或继续 throws)
+        System.out.println("姓名:" + p.getName());
+        System.out.println("年龄:" + p.getAge());
+    }
+}
+```
+
+> 📄 来源:`Demo03.java`
+
+### 3.3 throw 与 throws 的区别对比
+
+| 对比维度 | `throw` | `throws` |
+|---------|---------|----------|
+| **位置** | 方法体 **内部** | 方法声明 **后面** |
+| **后面跟的** | 异常 **对象**(`new XXXException()`) | 异常 **类名**(`XXXException`) |
+| **作用** | 手动 **抛出** 异常对象 | **声明** 方法可能抛出的异常 |
+| **数量** | 一次只能抛出一个异常对象 | 一次可以声明多个异常(用逗号分隔) |
+| **使用方式** | `throw new 异常类名();` | `throws 异常类名1, 异常类名2` |
+
+**一句话总结:**
+- **`throw`** — 在方法内部 **实际抛出** 一个异常("干活的")
+- **`throws`** — 在方法签名上 **声明** 有风险,提醒调用者去处理("贴告示的")
+
+> 📄 来源:`Demo03.java`
+
+### 3.4 自定义异常 + throw/throws 的完整调用链路
+
+```text
+【自定义异常类】                   AgeOutOfBoundsException extends Exception
+                                        ↑ 继承
+【Person.setAge()】               throw new AgeOutOfBoundsException("...")    ← 使用 throw 手动抛出
+                                        ↑ 声明
+                                  public void setAge(int age) throws AgeOutOfBoundsException
+                                        ↑ 调用
+【Demo03.main()】                 有 choices:
+                                    a) try-catch 处理
+                                    b) 继续 throws 给 JVM
+                                        ↑ 最终
+【JVM】                           默认处理:打印异常信息 + 终止程序
+```
+
+**处理选择:**
+- 调用 `setAge(20)` 时,编译期会提示该方法可能有异常
+- 可以选择 **try-catch 自己处理**
+- 也可以选择 **不处理,继续往上抛**(`throws`),最终抛给 JVM
+
+---
+
+---
+
+## 四、课后作业回顾 — StringUtilTest 字符串工具测试
 
 
-### 2.1 需求说明
+### 4.1 需求说明
 
 
 给定有前后空格和重复项的字符串数据 `"  张三, 李四, 王五, 赵六, 张三, 钱七  "`,需要:
 给定有前后空格和重复项的字符串数据 `"  张三, 李四, 王五, 赵六, 张三, 钱七  "`,需要:
 1. 去除每个元素的前后空格
 1. 去除每个元素的前后空格
 2. 去重(去除重复的姓名)
 2. 去重(去除重复的姓名)
 3. 统计每个姓名的出现次数
 3. 统计每个姓名的出现次数
 
 
-### 2.2 相关 API
+### 4.2 相关 API
 
 
 | 方法 | 说明 |
 | 方法 | 说明 |
 |------|------|
 |------|------|
@@ -154,7 +346,7 @@ public void method() throws IOException {
 | `String.trim()` | 去除字符串前后的空白字符 |
 | `String.trim()` | 去除字符串前后的空白字符 |
 | `Arrays.toString(Object[] a)` | 将数组转换为字符串表示形式 |
 | `Arrays.toString(Object[] a)` | 将数组转换为字符串表示形式 |
 
 
-### 2.3 核心算法
+### 4.3 核心算法
 
 
 #### 三步去重算法
 #### 三步去重算法
 
 
@@ -223,20 +415,20 @@ for (int i = 0; i < newArr.length; i++) {
 
 
 ---
 ---
 
 
-## 、课后作业回顾 — DataMathTest 日期时间计算
+## 、课后作业回顾 — DataMathTest 日期时间计算
 
 
-### 3.1 需求说明
+### 5.1 需求说明
 
 
 计算当前日期距离 2026-12-31 还有多少天。
 计算当前日期距离 2026-12-31 还有多少天。
 
 
-### 3.2 相关 API
+### 5.2 相关 API
 
 
 | 方法 | 说明 |
 | 方法 | 说明 |
 |------|------|
 |------|------|
 | `LocalDate.of(int year, int month, int dayOfMonth)` | 创建指定日期的 `LocalDate` 对象 |
 | `LocalDate.of(int year, int month, int dayOfMonth)` | 创建指定日期的 `LocalDate` 对象 |
 | `LocalDate.toEpochDay()` | 返回从 1970-01-01 到当前日期的天数 |
 | `LocalDate.toEpochDay()` | 返回从 1970-01-01 到当前日期的天数 |
 
 
-### 3.3 代码实现
+### 5.3 代码实现
 
 
 ```java
 ```java
 LocalDate localDate = LocalDate.of(2026, 7, 27);
 LocalDate localDate = LocalDate.of(2026, 7, 27);
@@ -257,17 +449,21 @@ System.out.println("距离 2026-12-31 还有:" + (epochDay - now) + "天");
 
 
 ---
 ---
 
 
-## 、随堂练习要点
+## 、随堂练习要点
 
 
-### 4.1 异常处理练习要点
+### 6.1 异常处理练习要点
 
 
 - 理解 `Throwable` → `Exception` → `RuntimeException` 的继承体系
 - 理解 `Throwable` → `Exception` → `RuntimeException` 的继承体系
 - 区分编译期异常和运行时异常
 - 区分编译期异常和运行时异常
 - 掌握 `try-catch` 多 catch 块的写法,注意异常类型顺序(子类→父类)
 - 掌握 `try-catch` 多 catch 块的写法,注意异常类型顺序(子类→父类)
 - 理解 `JVM` 默认处理异常的方式(打印信息 + 终止程序)
 - 理解 `JVM` 默认处理异常的方式(打印信息 + 终止程序)
+- 熟悉异常常用方法:`getMessage()`、`fillInStackTrace()`、`getCause()`
+- 掌握 `throw` 手动抛出异常对象(在方法体内部使用)
+- 掌握 `throws` 声明异常(在方法签名上使用),理解两者的区别
+- 掌握自定义异常的创建步骤(继承 Exception → 无参/有参构造方法)
 - 体会异常处理对程序 **鲁棒性(健壮性)** 的提升
 - 体会异常处理对程序 **鲁棒性(健壮性)** 的提升
 
 
-### 4.2 字符串处理综合练习要点
+### 6.2 字符串处理综合练习要点
 
 
 - 使用 `split(",")` 分割带分隔符的字符串
 - 使用 `split(",")` 分割带分隔符的字符串
 - 使用 `trim()` 去除字符串前后空格
 - 使用 `trim()` 去除字符串前后空格
@@ -275,7 +471,7 @@ System.out.println("距离 2026-12-31 还有:" + (epochDay - now) + "天");
 - 掌握元素出现次数的统计算法
 - 掌握元素出现次数的统计算法
 - 理解 `equals()` 在字符串比较中的必要性
 - 理解 `equals()` 在字符串比较中的必要性
 
 
-### 4.3 日期时间计算练习要点
+### 6.3 日期时间计算练习要点
 
 
 - 使用 `LocalDate.of()` 创建指定日期
 - 使用 `LocalDate.of()` 创建指定日期
 - 使用 `toEpochDay()` 计算两个日期之间的天数差
 - 使用 `toEpochDay()` 计算两个日期之间的天数差
@@ -283,10 +479,11 @@ System.out.println("距离 2026-12-31 还有:" + (epochDay - now) + "天");
 
 
 ---
 ---
 
 
-## 、拓展阅读
+## 、拓展阅读
 
 
-1. **Java 异常处理最佳实践** — 何时使用 checked vs unchecked exception
-2. **try-with-resources** — JDK7+ 自动关闭资源的异常处理方式
-3. **自定义异常** — 继承 `Exception` 或 `RuntimeException` 创建业务异常
-4. **`Period` 与 `Duration`** — Java 8 日期时间 API 中更灵活的时间间隔计算
-5. **`LinkedHashSet` 去重** — 利用集合类简化去重操作(替代手动数组去重)
+1. **Java 异常处理最佳实践** — 何时使用 checked vs unchecked exception,何时自定义异常
+2. **try-with-resources** — JDK7+ 自动关闭资源的异常处理方式(`try(Resource r = new Resource()){...}`)
+3. **异常链(Exception Chaining)** — 通过 `initCause()` 或构造方法将底层异常包装为高层异常
+4. **`Objects.requireNonNull()`** — 一种优雅避免 NullPointerException 的方式(JDK7+)
+5. **`Period` 与 `Duration`** — Java 8 日期时间 API 中更灵活的时间间隔计算
+6. **`LinkedHashSet` 去重** — 利用集合类简化去重操作(替代手动数组去重)