# 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` --- ## 三、随堂练习要点 ### 本日课程知识点概览 | 知识点 | 说明 | 对应文件 | |--------|------|---------| | 包装类概念 | 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` | ### 包装类重点 1. **概念理解**:为什么 Java 需要包装类?基本类型和包装类各有什么优劣? 2. **装箱/拆箱操作**:手动 vs 自动,理解编译器的底层转换逻辑 3. **实际应用**:在 Bean 类中优先使用包装类(如 `Integer` 替代 `int`),便于表达 `null` 语义 4. **常见陷阱**: - `==` 比较包装类时的缓存问题 - 包装类对象参与算术运算时的自动拆箱 - `null` 包装类拆箱会抛出 `NullPointerException` ### 集合框架重点 1. **理解集合 vs 数组的区别**:长度可变性、元素类型限制 2. **记忆体系结构**:Collection(List / Set)和 Map 两大分支 3. **掌握四种线性表结构**:栈(LIFO)、队列(FIFO)、数组(查询快增删慢)、链表(增删快查询慢) 4. **关联实现类**:`ArrayList` → 数组,`LinkedList` → 链表/队列/栈 5. **ArrayList 底层细节**: - 默认容量 10(懒加载,首次 `add` 才扩容) - 三个构造方法的区别 - `size` 与 `capacity` 的区别:`size` 是元素个数,`capacity` 是数组物理长度 - 允许存储 `null`,非线程安全 - `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) - 《Java 核心技术 卷I》第 3 章 — 包装器类与自动装箱 - 《Java 核心技术 卷I》第 9 章 — 集合