理解 final 关键字对数组引用与内容的不同影响
掌握 final 数组的合法操作与禁止操作
区分“引用不可变”与“对象内容不可变”的本质区别
避免在实际开发中因误解 final 而引发的逻辑错误
final 数组的核心概念在 Java 中,final 修饰的是变量的引用,而不是对象的内容。

允许:修改数组元素(如 arr[2] = 99)
禁止:让 arr 指向另一个数组(如 arr = new int[]{...})
关键理解:
final锁定的是“指针”,不是“指向的内容”
import java.util.Arrays;
public class FinalArrayDemo {
public static void main(String[] args) {
final int[] numbers = {1, 2, 3, 4, 5};
// 修改元素 —— 完全合法
numbers[0] = 10;
numbers[4] = 50;
System.out.println(Arrays.toString(numbers));
// 输出: [10, 2, 3, 4, 50]
}
}输出验证:数组内容已变,但
numbers仍指向原数组对象
public class ReassignmentError {
public static void main(String[] args) {
final int[] data = {100, 200};
// 下面这行会导致编译错误!
// data = new int[]{300, 400};
// Error: Cannot assign a value to final variable 'data'
}
}编译器提示:
Cannot assign a value to final variable 'data'
class Student {
String name;
int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
}
public class FinalObjectDemo {
public static void main(String[] args) {
final Student stu = new Student("张三", 85);
// 允许:修改对象状态
stu.score = 92;
stu.name = "李四";
System.out.println(stu.name + ": " + stu.score); // 李四: 92
// 禁止:stu = new Student("王五", 78); // 编译错误
}
}结论:
final对数组和普通对象的行为一致 —— 引用不变,内容可变
如果需要内容也不可变,可采用以下方案:
public class ImmutableIntArray {
private final int[] data;
public ImmutableIntArray(int[] input) {
this.data = input.clone(); // 防止外部修改原始数组
}
public int get(int index) {
return data[index];
}
public int length() {
return data.length;
}
// 不提供 set() 方法!
}import java.util.*;
List<Integer> immutableList = List.of(1, 2, 3, 4, 5);
// 或
List<Integer> safeList = Collections.unmodifiableList(Arrays.asList(1, 2, 3));这些集合在尝试修改时会抛出
UnsupportedOperationException
明确意图:用 final 表示“此引用不应被重新赋值”
避免混淆:不要误以为 final 能保护数据安全
防御性编程:若方法接收数组参数且不希望被修改,应复制一份
优先使用集合:对于需要不可变性的场景,考虑 List.of() 或 Collections.unmodifiableXxx()
final int[] arr → 引用不可变,内容可变
可以修改 arr[i],但不能执行 arr = ...
与 final 对象行为一致:字段可改,引用不可换
如需真正不可变数据结构,请使用不可变集合或自定义封装类
final 主要用于防止重赋值错误和表达设计语义
以下代码是否合法?为什么?
final int[][] matrix = {{1, 2}, {3, 4}};
matrix[0] = new int[]{5, 6}; // ?
matrix[0][0] = 99; // ?如果一个方法接收 final int[] data 作为参数,调用方能否通过该引用修改数组内容?这对 API 设计有何启示?
在多线程环境中,仅将数组声明为 final 能否保证线程安全?为什么?