Просмотр исходного кода

feat: 新增20260728课堂笔记及授课代码分析——Java异常处理(Throwable体系结构、try-catch多catch块、throws抛出、鲁棒性)+ 课后作业回顾(String分割去重统计、LocalDate天数差计算)

WanJL 5 часов назад
Родитель
Сommit
ada03e0a8f

+ 2 - 0
README.md

@@ -33,6 +33,7 @@
 | 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-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) |
 
 ## 二、授课代码
 
@@ -52,6 +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-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-28 | Java 异常处理(Throwable体系结构、try-catch多catch块、throws抛出、鲁棒性) + 课后作业回顾(String分割去重统计 + LocalDate天数差计算) | `c260728` | [`course`](./授课代码/c260728/src/course/) / [`homework260727`](./授课代码/c260728/src/homework260727/) |
 
 ## 三、课堂练习题
 

+ 30 - 0
授课代码/c260728/.gitignore

@@ -0,0 +1,30 @@
+### IntelliJ IDEA ###
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+.kotlin
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 91 - 0
授课代码/c260728/src/course/Demo01.java

@@ -0,0 +1,91 @@
+package course;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title Demo01
+ * @description Java异常
+ * @create 2026/7/28
+ */
+public class Demo01 {
+    /*
+    异常 就是程序出现不正常的情况
+    Java中的异常体系结构:
+            Throwable: 是Java中所有的异常类(Exception)和错误类(Error)的父类,
+                只有是它的对象或它的子类的对象,才可以别Java虚拟机进行处理(抛出、捕获处理)。
+                |- Exception:Java中所有异常类的父类
+                    |- 编译器异常 也就是在项目开发的时候,编译的时候就会报错。
+                    |- 运行时异常 也就是说代码不会报错,但是运行后会报错。所有的运行时异常都有一个父类 RuntimeException
+                |- Error:Java中所有错误类的父类
+             只有Java异常也就是Exception类或其子类,才可以别我们捕获处理或抛出,才可以处理。
+             至于Error这种错误,是内存级别的,不是我们可以捕获抛出的。需要改代码。
+                    public void method(){   //无条件的递归,一定会引发栈溢出StackOverflowError错误
+                        method();
+                    }
+
+            Throwable
+                |- Exception
+                      |- RuntimeException
+                            |-......
+                      |-.....
+                |- Error
+                    |-.....
+
+            Error 是属于严重问题,通常代码都无法处理。一般比如内存错误
+            Exception: 异常类,表示程序本身可以处理的问题。
+                RuntimeException以及其子类,属于运行时异常,比如空指针异常,数组索引下标越界异常,除零异常
+                除了RuntimeException以及其子类之外的所有异常,都是在编译期必须要处理的,否则程序不能通过编译
+
+                                    javac.exe                    java.exe
+            Java文件(源代码).java   ---编译--->  字节码文件 .class ---解释---> 运行结果
+                                      编译期        |              运行期
+                所谓的编译期异常,就是在Java源代码编译成字节码的过程中出现异常类,需要处理(捕获/抛出)
+                所谓的运行期异常,就是在编译的阶段没有任何错误,但是在从字节码文件解释运行的获取运行结果的期间出现的错误,导致程序不能正常执行。
+            Java虚拟机JVM 它会怎么处理异常?
+            如果程序出了问题,我们不做任何处理的话,最终会由JVM做默认的处理,处理的步骤:
+                1、把异常的名称、错误原因、以及异常出现的位置等信息输出在控制台
+                2、程序停止运行
+
+            我们处理异常的方式也分为两种:
+                我们自己捕获异常,并且处理异常。
+                   格式:
+                        try{
+                            可能出现异常的代码
+
+                        }catch(异常类型1 变量名){
+                            异常类型1的处理方式
+
+                        }catch(异常类型2 变量名){
+                            异常类型2的处理方式
+
+                        }catch(异常类型n 变量名){
+                            异常类型n的处理方式
+                        }
+
+                    我们处理异常,其实在开发场景下,怎么处理不太重要,能处理就处理了,处理不了也有让程序能够继续运行,才是最重要的。
+                    程序出现异常但能够处理或能够正常运行,体现的是程序的 鲁棒性(健壮性)
+                    兜住异常,给开发人员发邮件
+
+                我们不处理异常,交给调用者去处理,比如写了一个方法,里面会有编译期异常,我们不处理,只抛出。
+                谁调用了这个方法,谁去处理,他如果不处理也继续抛出,直到最终抛给JVM,JVM进行默认的处理。
+
+     */
+    public static void main(String[] args) {
+        int a=0;
+        try {   //这里面是可能出现异常的情况
+            //a = 10 / 0;
+            int[] arr={};
+            arr[3]=5;
+        }catch (ArithmeticException e){
+            System.out.println("不能除以0");
+            System.out.println("以及一些其他的异常处理操作.....");
+        }catch (RuntimeException e){
+            System.out.println("出现了运行时异常");
+        }catch (Exception e){
+            System.out.println("出现了异常..但不是运行时异常");
+        }
+        System.out.println(a);  //这种,代码写的时候不报错,但是运行后报错的,就是运行时异常
+        System.out.println("........后续的代码正在运行................");
+    }
+
+}

+ 69 - 0
授课代码/c260728/src/homework260727/p2_stringtool/StringUtilTest.java

@@ -0,0 +1,69 @@
+package homework260727.p2_stringtool;
+
+import java.util.Arrays;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title StringUtilTest
+ * @description
+ * @create 2026/7/28
+ */
+public class StringUtilTest {
+    public static void main(String[] args) {
+        String data = "  张三, 李四, 王五, 赵六, 张三, 钱七  ";
+        String names = "张三,李四,王五,赵六,钱七";
+
+        String[] split = data.split(",");
+        for (int i = 0; i < split.length; i++) {
+            split[i] = split[i].trim();
+        }
+        System.out.println(Arrays.toString(split));
+
+        int count = 0;  // 不重复元素的个数
+        // 第一步:统计不重复元素的个数
+        for (int i = 0; i < split.length; i++) {
+            boolean b = false;  // 标记:先认为当前元素没有重复
+            for (int j = 0; j < i; j++) {  // 检查 split[i] 在它之前是否出现过
+                if (split[i].equals(split[j])) {
+                    b = true;   // 找到重复,标记改为 true
+                    break;      // 跳出内层循环
+                }
+            }
+            if (b == false) {   // 没有重复,计数
+                count++;
+            }
+        }
+
+        // 第二步:创建新数组
+        String[] newArr = new String[count];
+        int index = 0;  // 新数组的索引
+
+        // 第三步:将不重复元素放入新数组
+        for (int i = 0; i < split.length; i++) {
+            boolean b = false;
+            for (int j = 0; j < i; j++) {
+                if (split[i].equals(split[j])) {
+                    b = true;
+                    break;
+                }
+            }
+            if (b == false) {           // 不重复的元素
+                newArr[index] = split[i]; // 放入新数组
+                index++;
+            }
+        }
+
+        System.out.println(Arrays.toString(newArr));
+
+        for (int i = 0; i < newArr.length; i++) {
+            int count02=0;
+            for (int j = 0; j < split.length; j++) {
+                if (newArr[i].equals(split[j])){
+                    count02++;
+                }
+            }
+            System.out.println(newArr[i]+":"+count02+"次");
+        }
+    }
+}

+ 25 - 0
授课代码/c260728/src/homework260727/p3_datamath/DataMathTest.java

@@ -0,0 +1,25 @@
+package homework260727.p3_datamath;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * @author WanJl
+ * @version 1.0
+ * @title DataMathTest
+ * @description
+ *
+ * 计算天数差计算当前日期距离 2026-12-31 还有多少天(用 `until()` 或 `toEpochDay()`)
+ * 距离 2026-12-31 还有:157 天
+ * @create 2026/7/28
+ */
+public class DataMathTest {
+    public static void main(String[] args) {
+        LocalDate localDate = LocalDate.of(2026, 7, 27);
+        LocalDate localDate2 = LocalDate.of(2026, 12, 31);
+        long now = localDate.toEpochDay();  //距离 1970-01-01 有多少天
+        long epochDay = localDate2.toEpochDay();  //距离 1970-01-01 有多少天
+        System.out.println("距离 2026-12-31 还有:"+(epochDay-now)+"天");
+
+    }
+}

+ 292 - 0
课堂笔记/01-JavaSE基础-笔记/20260728-笔记.md

@@ -0,0 +1,292 @@
+# JavaSE 基础 — Day12 笔记
+
+> **日期**:2026-07-28  
+> **项目**:`c260728`  
+> **作者**:WanJL
+
+---
+
+## 一、Java 异常处理(Exception Handling)
+
+### 1.1 异常的概念
+
+**异常(Exception)** 就是程序出现不正常的情况。
+
+在 Java 中,异常和错误都继承自 `Throwable` 类:
+
+```
+Throwable(顶级父类)
+    ├── Exception(异常类 — 程序本身可以处理的问题)
+    │     ├── RuntimeException(运行时异常)
+    │     │       ├── ArithmeticException(算术异常,如除零)
+    │     │       ├── ArrayIndexOutOfBoundsException(数组下标越界)
+    │     │       ├── NullPointerException(空指针异常)
+    │     │       └── ...
+    │     └── 非 RuntimeException(编译期异常 — 编译时必须处理)
+    │           ├── IOException
+    │           ├── SQLException
+    │           └── ...
+    └── Error(错误类 — 严重问题,代码无法处理)
+          ├── StackOverflowError(栈溢出,如无限制递归)
+          ├── OutOfMemoryError(内存溢出)
+          └── ...
+```
+
+> 📄 来源:`Demo01.java`
+
+**核心要点:**
+- **`Exception`** 及其子类可以被我们捕获处理或抛出,是程序可以处理的问题。
+- **`Error`** 是内存级别的严重问题,不是我们可以捕获抛出的,需要改代码。
+- 只有 `Exception` 类或其子类,才可以被我们捕获处理或抛出。
+
+### 1.2 编译期异常 vs 运行时异常
+
+| 类型 | 说明 | 示例 |
+|------|------|------|
+| **编译期异常**(Checked Exception) | 编译时就会报错,必须处理(捕获/抛出)才能通过编译 | `IOException`, `SQLException` |
+| **运行时异常**(Runtime Exception) | 编译时不报错,运行后才报错。所有运行时异常的父类是 `RuntimeException` | `ArithmeticException`, `NullPointerException`, `ArrayIndexOutOfBoundsException` |
+
+**Java 程序执行流程:**
+
+```text
+.java(源代码)  ---[javac 编译]--->  .class(字节码)  ---[java 解释]--->  运行结果
+                   编译期                                  运行期
+```
+
+- **编译期异常**:在 Java 源代码编译成字节码的过程中出现,必须处理。
+- **运行期异常**:编译阶段没有任何错误,但在从字节码文件解释运行获取结果期间出现错误。
+
+> 📄 来源:`Demo01.java`
+
+### 1.3 JVM 对异常的默认处理
+
+如果程序出了问题,我们不做任何处理,最终会由 **JVM 做默认处理**,处理步骤:
+
+1. 把 **异常的名称、错误原因、以及异常出现的位置** 等信息输出在控制台
+2. **程序停止运行**
+
+> 📄 来源:`Demo01.java`
+
+### 1.4 异常处理方式一:try-catch 捕获处理
+
+#### 语法格式
+
+```java
+try {
+    // 可能出现异常的代码
+} catch (异常类型1 变量名) {
+    // 异常类型1的处理方式
+} catch (异常类型2 变量名) {
+    // 异常类型2的处理方式
+} catch (异常类型n 变量名) {
+    // 异常类型n的处理方式
+}
+```
+
+#### 多个 catch 块的执行顺序
+
+- 多个 `catch` 块遵循 **从上到下匹配** 的原则
+- 一旦匹配到某个异常类型,就执行对应的 catch 块,然后跳出 try-catch 结构
+- 建议将 **子类异常放在前面,父类异常放在后面**,否则父类异常会"拦住"子类异常,导致子类 catch 块不可达
+
+#### 代码示例
+
+```java
+int a = 0;
+try {
+    int[] arr = {};
+    arr[3] = 5;  // 数组下标越界 → ArrayIndexOutOfBoundsException
+} catch (ArithmeticException e) {
+    System.out.println("不能除以0");
+    System.out.println("以及一些其他的异常处理操作.....");
+} catch (RuntimeException e) {
+    System.out.println("出现了运行时异常");
+} catch (Exception e) {
+    System.out.println("出现了异常..但不是运行时异常");
+}
+System.out.println(a);
+System.out.println("........后续的代码正在运行................");
+```
+
+> 📄 来源:`Demo01.java`
+
+**输出结果分析:** 由于 `arr[3] = 5` 抛出的是 `ArrayIndexOutOfBoundsException`,它是 `RuntimeException` 的子类,因此会被第二个 catch 块捕获,输出"出现了运行时异常",然后程序继续执行后续代码。
+
+### 1.5 异常处理方式二:throws 抛出
+
+我们不处理异常,交给调用者去处理。如果方法内包含编译期异常,可以不在方法内处理,只使用 `throws` 抛出:
+- 谁调用了这个方法,谁去处理
+- 调用者如果不处理,可以继续抛出
+- 最终抛给 JVM,JVM 进行默认的处理
+
+```java
+public void method() throws IOException {
+    // 方法体中的编译期异常不处理,交给调用者
+}
+```
+
+> 📄 来源:`Demo01.java`(概念说明)
+
+### 1.6 异常处理的编程思想 — 鲁棒性(健壮性)
+
+- **处理异常**:能处理就处理,处理不了的也要让程序能够继续运行,这才是最重要的
+- **鲁棒性(Robustness)**:程序出现异常但能够处理或能够正常运行,体现程序的健壮性
+- 实际开发场景:兜住异常,记录日志,给开发人员发邮件
+
+> 📄 来源:`Demo01.java`
+
+---
+
+## 二、课后作业回顾 — StringUtilTest 字符串工具测试
+
+### 2.1 需求说明
+
+给定有前后空格和重复项的字符串数据 `"  张三, 李四, 王五, 赵六, 张三, 钱七  "`,需要:
+1. 去除每个元素的前后空格
+2. 去重(去除重复的姓名)
+3. 统计每个姓名的出现次数
+
+### 2.2 相关 API
+
+| 方法 | 说明 |
+|------|------|
+| `String.split(String regex)` | 按指定正则表达式分割字符串为字符串数组 |
+| `String.trim()` | 去除字符串前后的空白字符 |
+| `Arrays.toString(Object[] a)` | 将数组转换为字符串表示形式 |
+
+### 2.3 核心算法
+
+#### 三步去重算法
+
+```java
+String data = "  张三, 李四, 王五, 赵六, 张三, 钱七  ";
+
+// 第一步:分割 + 去空格
+String[] split = data.split(",");
+for (int i = 0; i < split.length; i++) {
+    split[i] = split[i].trim();
+}
+
+// 第二步:统计不重复元素个数
+int count = 0;
+for (int i = 0; i < split.length; i++) {
+    boolean b = false;
+    for (int j = 0; j < i; j++) {
+        if (split[i].equals(split[j])) {
+            b = true;
+            break;
+        }
+    }
+    if (b == false) {
+        count++;
+    }
+}
+
+// 第三步:创建新数组,放入不重复元素
+String[] newArr = new String[count];
+int index = 0;
+for (int i = 0; i < split.length; i++) {
+    boolean b = false;
+    for (int j = 0; j < i; j++) {
+        if (split[i].equals(split[j])) {
+            b = true;
+            break;
+        }
+    }
+    if (b == false) {
+        newArr[index] = split[i];
+        index++;
+    }
+}
+```
+
+#### 统计元素出现次数
+
+```java
+for (int i = 0; i < newArr.length; i++) {
+    int count02 = 0;
+    for (int j = 0; j < split.length; j++) {
+        if (newArr[i].equals(split[j])) {
+            count02++;
+        }
+    }
+    System.out.println(newArr[i] + ":" + count02 + "次");
+}
+```
+
+> 📄 来源:`StringUtilTest.java`
+
+**关键知识点:**
+- `split[i].equals(split[j])` — 字符串比较使用 `equals()`,不能使用 `==`
+- 双重循环 + 标记位(`boolean b`)实现去重
+- 先统计个数再创建数组,避免数组长度不确定的问题
+
+---
+
+## 三、课后作业回顾 — DataMathTest 日期时间计算
+
+### 3.1 需求说明
+
+计算当前日期距离 2026-12-31 还有多少天。
+
+### 3.2 相关 API
+
+| 方法 | 说明 |
+|------|------|
+| `LocalDate.of(int year, int month, int dayOfMonth)` | 创建指定日期的 `LocalDate` 对象 |
+| `LocalDate.toEpochDay()` | 返回从 1970-01-01 到当前日期的天数 |
+
+### 3.3 代码实现
+
+```java
+LocalDate localDate = LocalDate.of(2026, 7, 27);
+LocalDate localDate2 = LocalDate.of(2026, 12, 31);
+long now = localDate.toEpochDay();       // 距离 1970-01-01 有多少天
+long epochDay = localDate2.toEpochDay(); // 距离 1970-01-01 有多少天
+System.out.println("距离 2026-12-31 还有:" + (epochDay - now) + "天");
+```
+
+> 📄 来源:`DataMathTest.java`
+
+**输出结果:** `距离 2026-12-31 还有:157 天`
+
+**关键知识点:**
+- `toEpochDay()` 返回的是 `long` 类型,表示从 1970-01-01(Epoch)到当前日期的天数
+- 两个日期的天数相减即可得到间隔天数
+- 也可以使用 `LocalDate.until(ChronoUnit.DAYS)` 或 `Period.between()` 实现
+
+---
+
+## 四、随堂练习要点
+
+### 4.1 异常处理练习要点
+
+- 理解 `Throwable` → `Exception` → `RuntimeException` 的继承体系
+- 区分编译期异常和运行时异常
+- 掌握 `try-catch` 多 catch 块的写法,注意异常类型顺序(子类→父类)
+- 理解 `JVM` 默认处理异常的方式(打印信息 + 终止程序)
+- 体会异常处理对程序 **鲁棒性(健壮性)** 的提升
+
+### 4.2 字符串处理综合练习要点
+
+- 使用 `split(",")` 分割带分隔符的字符串
+- 使用 `trim()` 去除字符串前后空格
+- 掌握数组去重算法(双重循环 + 标记位)
+- 掌握元素出现次数的统计算法
+- 理解 `equals()` 在字符串比较中的必要性
+
+### 4.3 日期时间计算练习要点
+
+- 使用 `LocalDate.of()` 创建指定日期
+- 使用 `toEpochDay()` 计算两个日期之间的天数差
+- 了解其他可选方案:`until()`、`Period.between()`、`ChronoUnit.DAYS.between()`
+
+---
+
+## 五、拓展阅读
+
+1. **Java 异常处理最佳实践** — 何时使用 checked vs unchecked exception
+2. **try-with-resources** — JDK7+ 自动关闭资源的异常处理方式
+3. **自定义异常** — 继承 `Exception` 或 `RuntimeException` 创建业务异常
+4. **`Period` 与 `Duration`** — Java 8 日期时间 API 中更灵活的时间间隔计算
+5. **`LinkedHashSet` 去重** — 利用集合类简化去重操作(替代手动数组去重)