# JavaSE 基础 — Day13 笔记 > **日期**:2026-07-29 > **项目**:`c260729` > **作者**:WanJL --- ## 一、包装类(Wrapper Classes) ### 1.1 为什么需要包装类? Java 中分为**基本数据类型**和**引用数据类型**。Java 的核心思想是「万事万物皆为对象」,对象就一定有属性和方法。但 Java 中的 8 种基本数据类型(`byte`、`short`、`int`、`long`、`float`、`double`、`char`、`boolean`)是从 C/C++ 延续过来的,并不符合 Java 的面向对象特性——它们没有属性、也没有方法。 为了解决这个问题,Java 为每个基本数据类型都设置了对应的引用数据类型,即**包装器类(Wrapper Classes)**。 > 📄 来源:`packaging/Demo01.java` ### 1.2 基本类型与包装类的对应关系 | 基本数据类型 | 包装器类(引用数据类型) | |-------------|------------------------| | `byte` | `Byte` | | `short` | `Short` | | `int` | `Integer` | | `long` | `Long` | | `float` | `Float` | | `double` | `Double` | | `char` | `Character` | | `boolean` | `Boolean` | 包装器类具有属性和方法,属于引用数据类型。 > 📄 来源:`packaging/Demo01.java` ### 1.3 装箱(Boxing)与拆箱(Unboxing) **装箱(Boxing)**:从基本数据类型 → 引用数据类型(包装类) **拆箱(Unboxing)**:从引用数据类型(包装类) → 基本数据类型 ```java // 手动装箱(JDK9 前的方式,之后已标记为过时) Integer integer = new Integer(a); // 装箱 // 自动装箱(JDK5+,推荐方式) Integer i = a; // int → Integer,自动装箱 Integer i1 = 15; // 直接赋值,自动装箱 // 手动拆箱 int a1 = integer.intValue(); // 调用 intValue() 方法拆箱 // 自动拆箱(JDK5+,推荐方式) int a2 = i; // Integer → int,自动拆箱 ``` > 📄 来源:`packaging/Demo01.java` ### 1.4 自动装箱与拆箱的底层原理 - **自动装箱**:编译器自动调用 `Integer.valueOf(int)` 方法 - **自动拆箱**:编译器自动调用 `Integer.intValue()` 方法 - 自动装箱/拆箱机制从 **JDK 5** 开始引入 ### 1.5 使用包装类的优势 1. **拥有属性和方法**:可以调用字符串解析、进制转换等方法 2. **泛型支持**:集合框架(如 `ArrayList`)必须使用包装类,不能使用基本类型 3. **允许 `null` 值**:包装类属于引用类型,可以为 `null`,这在数据库映射等场景中很有用 4. **工具方法**:提供了丰富的静态工具方法(如 `Integer.parseInt()`、`Integer.valueOf()` 等) ### 1.6 包装类在 Bean 类中的应用 在实际开发中,Bean 类(如 `Person`)的属性通常会使用包装类而非基本类型,原因包括: - 对象的属性可能为 `null`(数据库字段可为空) - 便于与泛型集合配合使用 - 统一使用引用数据类型,符合 OOP 风格 ```java public class Person { private String name; private Integer age; // 使用 Integer 而非 int,允许 age 为 null // getter / setter 方法 } ``` > 📄 来源:`packaging/Person.java` ### 1.7 包装类的常用方法 以 `Integer` 为例,包装类提供了一些非常实用的静态方法: | 方法 | 说明 | |------|------| | `Integer.parseInt(String s)` | 将字符串解析为 `int` | | `Integer.valueOf(int i)` | 将 `int` 转换为 `Integer`(自动装箱底层调用) | | `Integer.valueOf(String s)` | 将字符串转换为 `Integer` | | `int Integer.intValue()` | 将 `Integer` 转换为 `int`(自动拆箱底层调用) | | `Integer.MAX_VALUE` | `int` 的最大值(常量: 2147483647) | | `Integer.MIN_VALUE` | `int` 的最小值(常量: -2147483648) | 其他包装类(`Double`、`Boolean`、`Character` 等)也有类似的方法。 ### 1.8 包装类的缓存机制 部分包装类(`Integer`、`Byte`、`Short`、`Long`、`Character`)在自动装箱时存在**缓存机制**: - **Integer 缓存池**:默认缓存 `-128` ~ `127` 范围的整数 - 在此范围内,自动装箱复用同一个对象(`==` 比较为 `true`) - 超出此范围,每次自动装箱都会新建对象 ```java Integer a = 100; // 从缓存池获取 Integer b = 100; // 从缓存池获取 System.out.println(a == b); // true Integer c = 200; // 超出缓存范围,新建对象 Integer d = 200; // 超出缓存范围,新建对象 System.out.println(c == d); // false // 推荐使用 equals() 比较包装类对象的值 System.out.println(c.equals(d)); // true ``` > **注意**:包装类对象的值比较应始终使用 `equals()` 方法,不要使用 `==`。 --- ## 二、集合框架入门(Collection Framework) ### 2.1 集合的概念 **集合(Collection)** 是用来存储 Java 变量、对象的容器,也称为集合容器。 与数组一样,集合也是容器,可以存储多个数据。但二者有显著区别: | 对比维度 | 数组 | 集合 | |---------|------|------| | **长度** | 一旦创建,长度不可变 | 长度可变,可动态增减 | | **存储类型** | 可存储基本数据类型和引用数据类型 | **只能存储引用数据类型**(若需存基本类型,须用对应的包装类) | | **元素类型** | 只能存储同一种数据类型(类型固定) | 可存储多种数据类型(但建议存储单一类型,以便泛型约束) | > 📄 来源:`collection/Demo01.java` ### 2.2 集合的体系结构 Java 的集合框架分为两大派系: ``` Collection(单列集合) Map(双列集合) / \ / \ List Set HashMap TreeMap /\ /\ LinkedHashMap ArrayList HashSet LinkedList TreeSet Vector LinkedHashSet ``` - **Collection**、**Map**、**List**、**Set** 都是 **接口(Interface)**,只定义规则,不实现 - 具体功能由它们的 **实现类** 完成 **Collection(单列集合)**: - **List**(有序集合):元素有顺序,可重复 - `ArrayList`:数组结构实现 - `LinkedList`:链表结构实现 - `Vector`:早期版本,线程安全 - **Set**(无序集合):元素无顺序,不可重复 - `HashSet`:哈希表实现 - `TreeSet`:红黑树实现(可排序) - `LinkedHashSet`:可保持插入顺序 **Map(双列集合)**: - 由 **键(Key)** 和 **值(Value)** 组成 - **Key** 在集合中是唯一的,不能重复 - **Value** 可以重复 > 📄 来源:`collection/Demo01.java` ### 2.3 List 与底层数据结构 集合被称为**集合框架**,因为它把底层数据结构进行了封装。 **List** 直译为"列表",对应数据结构中的**线性表结构**。线性表结构大致分为四种: #### 2.3.1 栈结构(Stack) - **特点**:先进后出(LIFO — Last In First Out) - **操作术语**: - 加入元素 → **入栈 / 压栈(push)** - 移除元素 → **出栈 / 弹栈(pop)** ``` 入栈顺序: A → B → C 出栈顺序: C → B → A ``` #### 2.3.2 队列结构(Queue) - **特点**:先进先出(FIFO — First In First Out) ``` 入队顺序: A → B → C 出队顺序: A → B → C ``` #### 2.3.3 数组结构(Array) - 在内存中开辟一段**连续的空间**存储数据 - **优点**:查询速度快,可通过索引快速定位(`arr[index]`) - **缺点**:增删速度慢,因为需要移动后续元素 - **对应实现类**:`ArrayList` ``` 索引: [0] [1] [2] [3] [4] [5] 值: 10 20 30 40 50 60 ↑ 连续存储空间 ``` #### 2.3.4 链表结构(Linked List) - 在内存中**不连续**存储,不需要开辟连续的存储空间 - 每个节点(Node)包含数据 + 指向下一个节点的指针(地址) - **优点**:增删速度快,只需改变相邻节点的指针 - **缺点**:随机查询速度慢,需要从头结点依次遍历查找 - **对应实现类**:`LinkedList` ``` 头结点 → Node1 → Node2 → Node3 → null data data data next→ next→ next→ ``` #### 2.3.5 List 实现类的数据结构对应 | 实现类 | 对应数据结构 | 查询特点 | 增删特点 | |--------|-------------|---------|---------| | `ArrayList` | 数组(Array) | 快(索引定位) | 慢(元素移动) | | `LinkedList` | 链表(Linked List)、队列(Queue)、栈(Stack) | 慢(从头遍历) | 快(改指针) | > 📄 来源:`collection/Demo02.java` ### 2.4 ArrayList 底层实现详解 `ArrayList` 是 `List` 接口最常用的实现类,底层使用**可变数组**(动态数组)存储数据。 #### 2.4.1 核心特性 - **本质**:长度可变的数组,底层维护的是一个 `Object[] elementData` - **允许存储 `null`** - 和 `Vector` 类似,但**不是线程同步的**(非线程安全),性能更高 - 内部维护一个 `size` 属性,表示数组中**有效元素的个数**(不是数组长度) - `size()` 方法返回 `size` 属性 - `isEmpty()` 返回 `size == 0` > 📄 来源:`collection/Demo02.java` #### 2.4.2 默认容量与扩容机制 ```java // 默认初始容量为 10 private static final int DEFAULT_CAPACITY = 10; // 空数组(用户指定容量为 0 时使用) private static final Object[] EMPTY_ELEMENTDATA = {}; // 默认容量空数组(无参构造时使用) private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 底层维护的数组 transient Object[] elementData; ``` ![image-20260729111736848](https://gitee.com/lk506667090/PicGo_img/raw/master/images/202607291117007.png) **扩容流程:** 1. 使用无参构造时,`elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA`(容量为 0 的空数组) 2. 第一次添加元素时,立即将容量**扩容到 `DEFAULT_CAPACITY`(10)** 3. 后续元素超出容量时,自动按 **1.5 倍** 扩容(`oldCapacity + (oldCapacity >> 1)`) #### 2.4.3 三个构造方法 | 构造方法 | 说明 | |----------|------| | `ArrayList()` | 无参构造,`elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA`(初始容量 0,首次添加时扩容到 10) | | `ArrayList(int initialCapacity)` | 指定初始容量,若大于 0 则 `elementData = new Object[initialCapacity]`;若为 0 则 `elementData = EMPTY_ELEMENTDATA` | | `ArrayList(Collection c)` | 传入另一个集合对象,将其所有元素添加到新建的 `ArrayList` 中 | ```java // 无参构造 — 默认容量 10(懒加载,首次 add 时扩容) ArrayList list01 = new ArrayList<>(); // 指定初始容量为 20 ArrayList list02 = new ArrayList<>(20); // 以另一个集合为参数创建 ArrayList list03 = new ArrayList<>(list02); System.out.println(list01.size()); // 0 System.out.println(list02.size()); // 0 System.out.println(list03.size()); // 0 ``` > 📄 来源:`collection/Demo02.java` #### 2.4.4 关键属性速查 | 属性/方法 | 说明 | |-----------|------| | `DEFAULT_CAPACITY = 10` | 默认初始容量 | | `elementData` | 底层维护的 `Object[]` 数组 | | `size` | 有效元素的个数(非数组长度) | | `size()` | 返回 `size` 属性 | | `isEmpty()` | 返回 `size == 0` | > 💡 **注意**:`ArrayList` 的 `size` 是元素个数,`elementData.length` 才是数组的物理容量(`capacity`),二者不一定相等。`size <= capacity` 恒成立。 #### 2.4.5 ArrayList 扩容相关方法 ##### `trimToSize()` — 缩减容量 将底层数组的容量缩减为当前有效元素的个数,以节省内存空间。 ```java // 底层实现逻辑 elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); ``` - 如果 `size == 0`,将 `elementData` 设置为 `EMPTY_ELEMENTDATA`(空数组) - 否则,通过 `Arrays.copyOf()` 复制到新的数组,新数组长度为 `size` ##### `ensureCapacity(int minCapacity)` — 确保容量(手动扩容) 在向数组添加元素之前,都会先检查添加后的元素个数是否会超过当前数组长度。如果超过,就会扩容。数组扩容通过 `ensureCapacity()` 来实现。 这个方法用 `public` 修饰,开发者可以 **主动调用** 来手动扩容——当需要添加大量元素之前,提前扩容能提升性能。 ```java // 手动扩容到期望的最小容量 ArrayList list = new ArrayList<>(); list.ensureCapacity(100); // 提前扩容到 100,避免多次自动扩容 ``` **扩容触发条件**(两个条件同时满足才会扩容): 1. `minCapacity > elementData.length` —— 期望的最小容量大于当前数组长度 2. `!(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA && minCapacity <= DEFAULT_CAPACITY)` —— 既不是默认空数组,也不是期望容量 ≤ 默认容量 > 通俗解释:如果是刚 new 出来的空 `ArrayList`(`elementData` 为空数组),且期望容量没超过 10,不会立即扩容,等到第一次 `add` 时再扩容到 10。 ##### `grow(int minCapacity)` — 扩容核心方法 ```java Object[] grow(int minCapacity) { // oldCapacity: 旧的容量 // minCapacity - oldCapacity: 最小需要增加的容量 // oldCapacity >> 1: 最好增加的容量(旧容量除以 2) return Arrays.copyOf(elementData, ArraysSupport.newLength(oldCapacity, minCapacity - oldCapacity, oldCapacity >> 1)); } ``` **扩容计算公式:** | 变量 | 说明 | 示例(旧容量 = 10) | |------|------|-------------------| | `oldCapacity` | 旧容量 | 10 | | `minCapacity - oldCapacity` | 最小需要增加的容量 | 最少加 1 | | `oldCapacity >> 1` | 最好增加的容量 = 旧容量 ÷ 2 | `10 >> 1 = 5` | | **新容量** | `oldCapacity + oldCapacity >> 1` = **旧容量的 1.5 倍** | `10 + 5 = 15` | > ✅ **结论**:`ArrayList` 每次扩容时,新容量 = 旧容量 × **1.5 倍**(即 `oldCapacity + oldCapacity / 2`) ##### `main` 方法中的位移运算演示 ```java System.out.println(20 << 1); // 20 × 2 = 40 System.out.println(20 >> 1); // 20 ÷ 2 = 10 ``` - `<<`:左移,相当于乘以 2 - `>>`:右移,相当于除以 2 - `oldCapacity >> 1` 即为 `oldCapacity / 2` > 📄 来源:`collection/Demo02.java` ### 2.4.6 ArrayList 常用方法速查 | 方法 | 说明 | |------|------| | `add(E e)` | 向集合末尾添加元素 | | `add(int index, E element)` | 向集合指定位置插入元素 | | `get(int index)` | 获取指定索引位置的元素 | | `getFirst()` | 获取集合第一个元素(JDK 21+) | | `getLast()` | 获取集合最后一个元素(JDK 21+) | | `set(int index, E element)` | 修改指定索引位置的元素 | | `remove(int index)` | 删除指定索引位置的元素 | | `remove(Object o)` | 删除第一个匹配的指定元素 | | `size()` | 获取集合中元素的个数 | | `isEmpty()` | 判断集合是否为空(`size == 0`) | | `contains(Object o)` | 判断集合是否包含指定元素 | | `indexOf(Object o)` | 返回指定元素首次出现的索引,不存在则返回 -1 | | `toArray()` | 将集合转换为 `Object[]` 数组 | | `clear()` | 清空集合中的所有元素 | ```java ArrayList list = new ArrayList<>(); list.add("Java"); // 添加元素 list.add(0, "Python"); // 在索引 0 插入 list.get(0); // 获取索引 0 的元素 list.getFirst(); // 获取第一个元素 list.getLast(); // 获取最后一个元素 list.set(0, "C++"); // 修改索引 0 的元素 list.remove(0); // 删除索引 0 的元素 list.remove("Java"); // 按元素删除 list.size(); // 元素个数 list.isEmpty(); // 是否为空 list.contains("Java"); // 是否包含 list.indexOf("Java"); // 查找索引 Object[] arr = list.toArray(); // 转数组 list.clear(); // 清空 ``` > 📄 来源:`collection/Demo02.java` --- ## 三、泛型(Generics) ### 3.1 泛型的概念 **泛型(Generics)** 是 JDK 1.5 引入的特性,它提供了**编译时进行类型安全检测**的机制。 **为什么需要泛型?** 在没有泛型之前,`ArrayList` 可以存储任意类型的对象: ```java ArrayList list = new ArrayList(); list.add(new Person()); // Person 对象 list.add(new Student()); // Student 对象 list.add(new Dog()); // Dog 对象 // 遍历时,需要强制类型转换,容易发生 ClassCastException for (int i = 0; i < list.size(); i++) { Person p = (Person) list.get(i); // 遍历到 Dog 时抛出异常! } ``` > 📄 来源:`collection/Demo03.java` ### 3.2 泛型的好处 1. **把运行期间的问题,提前到了编译期间** — 编译时就能发现类型不匹配 2. **避免了强制类型转换** — 编译器自动处理类型转换 ```java // 使用泛型后,编译时就能检查类型 ArrayList list = new ArrayList<>(); list.add("张三"); list.add("李四"); // list.add(666); // 编译报错!只能添加 String 类型 // 遍历时无需强制类型转换 for (int i = 0; i < list.size(); i++) { String s = list.get(i); // 自动识别为 String 类型 } ``` > 📄 来源:`collection/Demo03.java` ### 3.3 泛型的定义格式 #### 单类型参数 ```java <类型> // 如 ,尖括号里面可以写任意字母,一般只写一个字母 ``` `ArrayList` 的源码定义: ```java public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { // 使用的就是 表示这个 ArrayList 要操作的元素类型 } ``` #### 自定义泛型类 ```java public class MyArray { public void add(A a) { // ... 传入的类型由使用时决定 } } // 使用: MyArray arr1 = new MyArray<>(); arr1.add(10); // 必须是 Integer MyArray arr2 = new MyArray<>(); arr2.add("Hello"); // 必须是 String ``` **完整示例** — `PrintX` 泛型类: ```java public class PrintX { public void print(X x) { System.out.println(x.toString()); } public X getX(X x) { return x; } public void printList(List list) { // 使用无界通配符 ? } } // 使用: PrintX px = new PrintX<>(); Person person = new Person(); person.setAge(25); person.setName("张三"); px.print(person); // 输出 Person 对象的 toString() ``` > 📄 来源:`collection/PrintX.java`、`collection/Test.java` #### 多类型参数 可以指定多种类型,中间使用逗号分隔: ```java <类型1, 类型2, ..., 类型n> // 例如: ``` ### 3.4 泛型的通配符(Wildcard) | 通配符 | 名称 | 说明 | 示例 | |--------|------|------|------| | `?` | **无界通配符**(Unbounded Wildcard) | 表示未知类型,可以接收任意类型 | `public void printList(List list)` | | `? extends T` | **上界通配符**(Upper Bounded Wildcard) | 表示类型是 T 或 T 的子类 | `public void printList(List list)` | | `? super T` | **下界通配符**(Lower Bounded Wildcard) | 表示类型是 T 或 T 的父类 | `public void add(List list)` | ```java // 无界通配符 public void printList(List list) { for (Object obj : list) { System.out.println(obj); } } // 上界通配符 — 只允许 Person 及其子类 public void processPersons(List list) { for (Person p : list) { System.out.println(p.getName()); } } ``` > 📄 来源:`collection/Demo03.java` ### 3.5 有界类型参数(Bounded Type Parameter) 使用 `extends` 关键字限制泛型的类型范围: ```java // 使用时,传入的类型只能是该父类的子类 ``` ```java public class GenericMethod { // 只允许 Number 及其子类(Integer、Double 等) public static double sum(T a, T b) { return a.doubleValue() + b.doubleValue(); } } // 使用: double result = GenericMethod.sum(10, 20); // Integer ✓ double result2 = GenericMethod.sum(3.14, 2.5); // Double ✓ // GenericMethod.sum("A", "B"); // 编译报错!String 不是 Number 的子类 ``` ### 3.6 泛型在集合中的实际应用 ```java // 带泛型的 ArrayList ArrayList list1 = new ArrayList<>(); // 只能存 String ArrayList list2 = new ArrayList<>(); // 只能存 Person ArrayList list3 = new ArrayList<>(); // 只能存 Integer // LinkedList 同样支持泛型 LinkedList linkedList = new LinkedList<>(); ``` > 📄 来源:`collection/Test.java` ### 3.7 课堂练习代码参考 课程配套的两个练习参考答案,展示了 Student 类的完整实现: **Student 类** — 重写了 `toString()`、`equals()`、`hashCode()` 方法: | 方法 | 用途 | |------|------| | `toString()` | 方便打印学生信息 | | `equals(Object)` | 基于学号判断两个学生是否相等 | | `hashCode()` | 基于学号生成哈希值 | ```java // equals() 方法 — 基于学号比较 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Student student = (Student) obj; return studentId != null && studentId.equals(student.studentId); } ``` > 📄 来源:`exercises/exercise01/Student.java`、`exercises/exercise02/Student.java` **注意**:`exercise02` 中的 `StudentManager` 使用了**未使用泛型**的原始类型写法: ```java ArrayList students = new ArrayList<>(); // 未指定泛型 // 遍历时需要通过 instanceof 进行类型判断 for (int i = 0; i < students.size(); i++) { Object o = students.get(i); if (o instanceof Student s) { // JDK 16+ 模式匹配 System.out.println(s); } } ``` > 💡 **对比**:`exercise01` 使用了泛型 `ArrayList`,`exercise02` 使用原始类型。推荐始终使用泛型以享受编译时类型检查的好处。 > 📄 来源:`exercises/exercise01/StudentManager.java`、`exercises/exercise02/StudentManager.java` --- ## 四、随堂练习要点 ### 本日课程知识点概览 | 知识点 | 说明 | 对应文件 | |--------|------|---------| | 包装类概念 | 8种基本类型→包装类,装箱/拆箱 | `packaging/Demo01.java` | | 包装类在 Bean 中的应用 | `Integer` 替代 `int` | `packaging/Person.java` | | 包装类缓存机制 | -128~127 缓存池,`==` vs `equals()` | — | | 集合框架概述 | 集合 vs 数组对比,体系结构 | `collection/Demo01.java` | | 线性表数据结构 | 栈/队列/数组/链表 特点 | `collection/Demo02.java` | | **ArrayList 底层原理** | **可变数组、默认容量10、扩容机制、三个构造方法、size vs capacity** | `collection/Demo02.java` | | **ArrayList 扩容方法** | **trimToSize 缩减容量、ensureCapacity 手动扩容、grow 1.5倍扩容公式** | `collection/Demo02.java` | | **位运算符** | **`>>` 右移(除2)、`<<` 左移(乘2)在扩容中的使用** | `collection/Demo02.java` | | **ArrayList 常用方法** | **add/get/set/remove/size/isEmpty/contains/indexOf/toArray/getFirst/getLast** | `collection/Demo02.java` | | **泛型(Generics)概念** | **编译时类型安全检测、避免强制类型转换** | `collection/Demo03.java` | | **泛型定义格式** | **单类型 ``、多类型 ``、有界类型 ``** | `collection/Demo03.java` | | **泛型通配符** | **`?` 无界通配符、`? extends T` 上界通配符** | `collection/Demo03.java` | | **自定义泛型类** | **PrintX\、print(X x)、getX(X x)、printList(List\)** | `collection/PrintX.java` | | **练习参考答案** | **Student 类重写 toString/equals/hashCode、泛型 vs 原始类型对比** | `exercise01/*.java`、`exercise02/*.java` | ### 包装类重点 1. **概念理解**:为什么 Java 需要包装类?基本类型和包装类各有什么优劣? 2. **装箱/拆箱操作**:手动 vs 自动,理解编译器的底层转换逻辑 3. **实际应用**:在 Bean 类中优先使用包装类(如 `Integer` 替代 `int`),便于表达 `null` 语义 4. **常见陷阱**: - `==` 比较包装类时的缓存问题 - 包装类对象参与算术运算时的自动拆箱 - `null` 包装类拆箱会抛出 `NullPointerException` ### 泛型重点 1. **理解泛型的必要性**:解决了原始集合类型不安全、需要强制类型转换的问题 2. **区分三种通配符**:`?`(无界)、`? extends T`(上界)、`? super T`(下界) 3. **掌握泛型定义语法**:``、``、`` 4. **泛型 VS 原始类型**:始终优先使用泛型(`ArrayList`),避免原始类型 5. **模式匹配 instanceof**:JDK 16+ 支持 `if (o instanceof Student s)` 自动变量绑定 ### 集合框架重点 1. **理解集合 vs 数组的区别**:长度可变性、元素类型限制 2. **记忆体系结构**:Collection(List / Set)和 Map 两大分支 3. **掌握四种线性表结构**:栈(LIFO)、队列(FIFO)、数组(查询快增删慢)、链表(增删快查询慢) 4. **关联实现类**:`ArrayList` → 数组,`LinkedList` → 链表/队列/栈 5. **ArrayList 底层细节**: - 默认容量 10(懒加载,首次 `add` 才扩容) - 三个构造方法的区别 - `size` 与 `capacity` 的区别:`size` 是元素个数,`capacity` 是数组物理长度 - 允许存储 `null`,非线程安全 - **ArrayList 常用方法**:`add()`/`get()`/`set()`/`remove()`/`size()`/`isEmpty()`/`contains()`/`indexOf()`/`toArray()`/`getFirst()`/`getLast()` - `trimToSize()`:缩减容量到有效元素个数 - `ensureCapacity()`:手动提前扩容优化性能 - `grow()`:每次扩容为旧容量的 **1.5 倍**(`oldCapacity + oldCapacity >> 1`) - `>>` 右移位运算等价于除以 2 --- ## 四、拓展阅读 - [Oracle Java 官方文档 — Autoboxing and Unboxing](https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html) - [Java 包装类缓存机制详解](https://www.baeldung.com/java-integer-cache) - [Oracle Java 官方文档 — Collections Framework Overview](https://docs.oracle.com/javase/8/docs/technotes/guides/collections/overview.html) - [Java 集合框架详解:List、Set、Map 的区别](https://www.baeldung.com/java-collections) - [Oracle Java 官方文档 — Generics Tutorial](https://docs.oracle.com/javase/tutorial/java/generics/index.html) - [Java 泛型:通配符详解](https://www.baeldung.com/java-generics-advanced) - 《Java 核心技术 卷I》第 3 章 — 包装器类与自动装箱 - 《Java 核心技术 卷I》第 5 章 — 泛型 - 《Java 核心技术 卷I》第 9 章 — 集合