日期:2026-07-29
项目:c260729
作者:WanJL
Java 中分为基本数据类型和引用数据类型。Java 的核心思想是「万事万物皆为对象」,对象就一定有属性和方法。但 Java 中的 8 种基本数据类型(byte、short、int、long、float、double、char、boolean)是从 C/C++ 延续过来的,并不符合 Java 的面向对象特性——它们没有属性、也没有方法。
为了解决这个问题,Java 为每个基本数据类型都设置了对应的引用数据类型,即包装器类(Wrapper Classes)。
📄 来源:
packaging/Demo01.java
| 基本数据类型 | 包装器类(引用数据类型) |
|---|---|
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
char |
Character |
boolean |
Boolean |
包装器类具有属性和方法,属于引用数据类型。
📄 来源:
packaging/Demo01.java
装箱(Boxing):从基本数据类型 → 引用数据类型(包装类)
拆箱(Unboxing):从引用数据类型(包装类) → 基本数据类型
// 手动装箱(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
Integer.valueOf(int) 方法Integer.intValue() 方法ArrayList<Integer>)必须使用包装类,不能使用基本类型null 值:包装类属于引用类型,可以为 null,这在数据库映射等场景中很有用Integer.parseInt()、Integer.valueOf() 等)在实际开发中,Bean 类(如 Person)的属性通常会使用包装类而非基本类型,原因包括:
null(数据库字段可为空)统一使用引用数据类型,符合 OOP 风格
public class Person {
private String name;
private Integer age; // 使用 Integer 而非 int,允许 age 为 null
// getter / setter 方法
}
📄 来源:
packaging/Person.java
以 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 等)也有类似的方法。
部分包装类(Integer、Byte、Short、Long、Character)在自动装箱时存在缓存机制:
-128 ~ 127 范围的整数== 比较为 true)超出此范围,每次自动装箱都会新建对象
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) 是用来存储 Java 变量、对象的容器,也称为集合容器。
与数组一样,集合也是容器,可以存储多个数据。但二者有显著区别:
| 对比维度 | 数组 | 集合 |
|---|---|---|
| 长度 | 一旦创建,长度不可变 | 长度可变,可动态增减 |
| 存储类型 | 可存储基本数据类型和引用数据类型 | 只能存储引用数据类型(若需存基本类型,须用对应的包装类) |
| 元素类型 | 只能存储同一种数据类型(类型固定) | 可存储多种数据类型(但建议存储单一类型,以便泛型约束) |
📄 来源:
collection/Demo01.java
Java 的集合框架分为两大派系:
Collection(单列集合) Map(双列集合)
/ \ / \
List Set HashMap TreeMap
/\ /\ LinkedHashMap
ArrayList HashSet
LinkedList TreeSet
Vector LinkedHashSet
Collection(单列集合):
ArrayList:数组结构实现LinkedList:链表结构实现Vector:早期版本,线程安全HashSet:哈希表实现TreeSet:红黑树实现(可排序)LinkedHashSet:可保持插入顺序Map(双列集合):
📄 来源:
collection/Demo01.java
集合被称为集合框架,因为它把底层数据结构进行了封装。
List 直译为"列表",对应数据结构中的线性表结构。线性表结构大致分为四种:
操作术语:
移除元素 → 出栈 / 弹栈(pop)
入栈顺序: A → B → C
出栈顺序: C → B → A
特点:先进先出(FIFO — First In First Out)
入队顺序: A → B → C
出队顺序: A → B → C
arr[index])对应实现类:ArrayList
索引: [0] [1] [2] [3] [4] [5]
值: 10 20 30 40 50 60
↑ 连续存储空间
对应实现类:LinkedList
头结点 → Node1 → Node2 → Node3 → null
data data data
next→ next→ next→
| 实现类 | 对应数据结构 | 查询特点 | 增删特点 |
|---|---|---|---|
ArrayList |
数组(Array) | 快(索引定位) | 慢(元素移动) |
LinkedList |
链表(Linked List)、队列(Queue)、栈(Stack) | 慢(从头遍历) | 快(改指针) |
📄 来源:
collection/Demo02.java
ArrayList 是 List 接口最常用的实现类,底层使用可变数组(动态数组)存储数据。
Object[] elementDatanullVector 类似,但不是线程同步的(非线程安全),性能更高size 属性,表示数组中有效元素的个数(不是数组长度)size() 方法返回 size 属性isEmpty() 返回 size == 0📄 来源:
collection/Demo02.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;
扩容流程:
elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(容量为 0 的空数组)DEFAULT_CAPACITY(10)oldCapacity + (oldCapacity >> 1))| 构造方法 | 说明 |
|---|---|
ArrayList() |
无参构造,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(初始容量 0,首次添加时扩容到 10) |
ArrayList(int initialCapacity) |
指定初始容量,若大于 0 则 elementData = new Object[initialCapacity];若为 0 则 elementData = EMPTY_ELEMENTDATA |
ArrayList(Collection<? extends E> c) |
传入另一个集合对象,将其所有元素添加到新建的 ArrayList 中 |
// 无参构造 — 默认容量 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
| 属性/方法 | 说明 |
|---|---|
DEFAULT_CAPACITY = 10 |
默认初始容量 |
elementData |
底层维护的 Object[] 数组 |
size |
有效元素的个数(非数组长度) |
size() |
返回 size 属性 |
isEmpty() |
返回 size == 0 |
💡 注意:
ArrayList的size是元素个数,elementData.length才是数组的物理容量(capacity),二者不一定相等。size <= capacity恒成立。
trimToSize() — 缩减容量将底层数组的容量缩减为当前有效元素的个数,以节省内存空间。
// 底层实现逻辑
elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
size == 0,将 elementData 设置为 EMPTY_ELEMENTDATA(空数组)Arrays.copyOf() 复制到新的数组,新数组长度为 sizeensureCapacity(int minCapacity) — 确保容量(手动扩容)在向数组添加元素之前,都会先检查添加后的元素个数是否会超过当前数组长度。如果超过,就会扩容。数组扩容通过 ensureCapacity() 来实现。
这个方法用 public 修饰,开发者可以 主动调用 来手动扩容——当需要添加大量元素之前,提前扩容能提升性能。
// 手动扩容到期望的最小容量
ArrayList list = new ArrayList<>();
list.ensureCapacity(100); // 提前扩容到 100,避免多次自动扩容
扩容触发条件(两个条件同时满足才会扩容):
minCapacity > elementData.length —— 期望的最小容量大于当前数组长度!(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA && minCapacity <= DEFAULT_CAPACITY) —— 既不是默认空数组,也不是期望容量 ≤ 默认容量通俗解释:如果是刚 new 出来的空
ArrayList(elementData为空数组),且期望容量没超过 10,不会立即扩容,等到第一次add时再扩容到 10。
grow(int minCapacity) — 扩容核心方法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 方法中的位移运算演示System.out.println(20 << 1); // 20 × 2 = 40
System.out.println(20 >> 1); // 20 ÷ 2 = 10
<<:左移,相当于乘以 2>>:右移,相当于除以 2oldCapacity >> 1 即为 oldCapacity / 2📄 来源:
collection/Demo02.java
| 方法 | 说明 |
|---|---|
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() |
清空集合中的所有元素 |
ArrayList<String> 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) 是 JDK 1.5 引入的特性,它提供了编译时进行类型安全检测的机制。
为什么需要泛型?
在没有泛型之前,ArrayList 可以存储任意类型的对象:
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
避免了强制类型转换 — 编译器自动处理类型转换
// 使用泛型后,编译时就能检查类型
ArrayList<String> 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
<类型> // 如 <E>、<T>,尖括号里面可以写任意字母,一般只写一个字母
ArrayList 的源码定义:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 使用的就是 <E> 表示这个 ArrayList 要操作的元素类型
}
public class MyArray<A> {
public void add(A a) {
// ... 传入的类型由使用时决定
}
}
// 使用:
MyArray<Integer> arr1 = new MyArray<>();
arr1.add(10); // 必须是 Integer
MyArray<String> arr2 = new MyArray<>();
arr2.add("Hello"); // 必须是 String
完整示例 — PrintX<X> 泛型类:
public class PrintX<X> {
public void print(X x) {
System.out.println(x.toString());
}
public X getX(X x) {
return x;
}
public void printList(List<?> list) {
// 使用无界通配符 ?
}
}
// 使用:
PrintX<Person> px = new PrintX<>();
Person person = new Person();
person.setAge(25);
person.setName("张三");
px.print(person); // 输出 Person 对象的 toString()
📄 来源:
collection/PrintX.java、collection/Test.java
可以指定多种类型,中间使用逗号分隔:
<类型1, 类型2, ..., 类型n>
// 例如:
<K,V> <E,T> <A,B>
| 通配符 | 名称 | 说明 | 示例 |
|---|---|---|---|
? |
无界通配符(Unbounded Wildcard) | 表示未知类型,可以接收任意类型 | public void printList(List<?> list) |
? extends T |
上界通配符(Upper Bounded Wildcard) | 表示类型是 T 或 T 的子类 | public void printList(List<? extends Person> list) |
? super T |
下界通配符(Lower Bounded Wildcard) | 表示类型是 T 或 T 的父类 | public void add(List<? super Integer> list) |
// 无界通配符
public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
// 上界通配符 — 只允许 Person 及其子类
public void processPersons(List<? extends Person> list) {
for (Person p : list) {
System.out.println(p.getName());
}
}
📄 来源:
collection/Demo03.java
使用 extends 关键字限制泛型的类型范围:
<T extends 某个父类>
// 使用时,传入的类型只能是该父类的子类
public class GenericMethod {
// 只允许 Number 及其子类(Integer、Double 等)
public static <T extends Number> 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 的子类
// 带泛型的 ArrayList
ArrayList<String> list1 = new ArrayList<>(); // 只能存 String
ArrayList<Person> list2 = new ArrayList<>(); // 只能存 Person
ArrayList<Integer> list3 = new ArrayList<>(); // 只能存 Integer
// LinkedList 同样支持泛型
LinkedList<Person> linkedList = new LinkedList<>();
📄 来源:
collection/Test.java
课程配套的两个练习参考答案,展示了 Student 类的完整实现:
Student 类 — 重写了 toString()、equals()、hashCode() 方法:
| 方法 | 用途 |
|---|---|
toString() |
方便打印学生信息 |
equals(Object) |
基于学号判断两个学生是否相等 |
hashCode() |
基于学号生成哈希值 |
// 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 使用了未使用泛型的原始类型写法:
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<Student>,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 |
| 泛型定义格式 | 单类型 <E>、多类型 <K,V>、有界类型 <T extends 父类> |
collection/Demo03.java |
| 泛型通配符 | ? 无界通配符、? extends T 上界通配符 |
collection/Demo03.java |
| 自定义泛型类 | PrintX<X>、print(X x)、getX(X x)、printList(List<?>) | collection/PrintX.java |
| 练习参考答案 | Student 类重写 toString/equals/hashCode、泛型 vs 原始类型对比 | exercise01/*.java、exercise02/*.java |
Integer 替代 int),便于表达 null 语义== 比较包装类时的缓存问题null 包装类拆箱会抛出 NullPointerException?(无界)、? extends T(上界)、? super T(下界)<T>、<K,V>、<T extends 父类>ArrayList<Student>),避免原始类型if (o instanceof Student s) 自动变量绑定ArrayList → 数组,LinkedList → 链表/队列/栈add 才扩容)size 与 capacity 的区别:size 是元素个数,capacity 是数组物理长度null,非线程安全add()/get()/set()/remove()/size()/isEmpty()/contains()/indexOf()/toArray()/getFirst()/getLast()trimToSize():缩减容量到有效元素个数ensureCapacity():手动提前扩容优化性能grow():每次扩容为旧容量的 1.5 倍(oldCapacity + oldCapacity >> 1)>> 右移位运算等价于除以 2