Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
      • 1. 数组的核心特点
      • 2. 声明与初始化
      • 3. 默认值
      • 4. 访问与遍历
      • 5. 多维数组
      • 6. 数组复制
        • 6.1 浅拷贝问题
      • 7. Arrays 工具类
      • 8. 数组与可变参数
      • 9. 数组与集合的区别
      • 10. Arrays.asList 的坑
      • 11. 常见异常
        • 11.1 数组越界
        • 11.2 空指针
        • 11.3 数组存储异常
      • Tips 快问快答
    • String字符串
    • 异常处理
    • 基本类型与包装类
    • Object类与常用方法
    • 泛型
    • 枚举与注解
    • 反射
    • 值传递与引用
    • Lambda表达式与函数式接口
    • Stream API
    • Optional与空值处理
    • 日期时间API
    • Java现代语法特性
  • Java集合

  • Java并发

  • Java IO

  • JVM

  • Java
  • Java基础
Wray
2026-06-24
目录

Java数组

数组是 Java 中最基础的数据结构之一,用于保存一组类型相同、长度固定的元素。它看起来简单,但涉及内存布局、默认值、多维结构、浅拷贝、边界检查、泛型限制等很多基础知识。

在实际开发中,数组常出现在以下场景:

  • 方法参数,例如 main(String[] args)。
  • 底层容器实现,例如 ArrayList 内部使用数组。
  • 批量数据处理,例如缓存、缓冲区、动态规划。
  • JVM 和框架底层,例如方法参数、反射调用、字节数组。

# 1. 数组的核心特点

特点 说明
类型固定 一个数组只能保存同一种元素类型
长度固定 创建后 length 不会变化
索引访问 通过下标访问,时间复杂度 O(1)
下标从 0 开始 第一个元素是 array[0]
数组本身是对象 数组有对象头、类型信息和 length 属性
边界检查 越界访问会抛出 ArrayIndexOutOfBoundsException

数组变量保存的是引用,数组对象位于堆中。

int[] numbers = new int[3];

内存关系:

栈帧
┌────────────────┐
│ numbers 引用    │
└───────┬────────┘
        │
        ▼
堆内存
┌─────────────────────────┐
│ int[3]                  │
│ length = 3              │
│ [0] = 0                 │
│ [1] = 0                 │
│ [2] = 0                 │
└─────────────────────────┘

# 2. 声明与初始化

推荐写法:

int[] numbers;
String[] names;

不推荐但合法的写法:

int numbers[];

创建指定长度数组:

int[] scores = new int[5];

静态初始化:

int[] scores = {90, 85, 70};

动态初始化:

String[] names = new String[3];
names[0] = "Tom";
names[1] = "Jerry";
names[2] = "Alice";

匿名数组:

printScores(new int[] {90, 80, 70});

错误写法:

int[] scores;
scores = {90, 80, 70}; // 编译失败

分开声明和赋值时,应写成:

int[] scores;
scores = new int[] {90, 80, 70};

# 3. 默认值

数组元素会自动初始化为类型默认值。

元素类型 默认值
byte、short、int、long 0
float、double 0.0
char '\u0000'
boolean false
引用类型 null

引用类型数组容易出现空指针:

User[] users = new User[3];
users[0].getName(); // NullPointerException

正确做法:

User[] users = new User[3];
users[0] = new User("Tom");
System.out.println(users[0].getName());

引用数组内存:

栈帧
┌─────────────┐
│ users 引用   │
└──────┬──────┘
       ▼
堆内存
┌───────────────────────┐
│ User[3]               │
│ [0] ───────────────┐  │
│ [1] = null         │  │
│ [2] = null         │  │
└────────────────────┼──┘
                     ▼
              ┌────────────┐
              │ User("Tom")│
              └────────────┘

# 4. 访问与遍历

通过下标访问:

int first = scores[0];
scores[1] = 100;

普通 for 循环:

for (int i = 0; i < scores.length; i++) {
    System.out.println(scores[i]);
}

增强 for 循环:

for (int score : scores) {
    System.out.println(score);
}

两者区别:

方式 适合场景 是否能拿到下标 是否适合修改元素
普通 for 需要下标、需要修改 可以 可以
增强 for 只读遍历 不可以 基本类型不能直接修改原数组元素

示例:

int[] numbers = {1, 2, 3};

for (int number : numbers) {
    number = number * 2;
}

System.out.println(Arrays.toString(numbers)); // [1, 2, 3]

增强 for 中的 number 是元素值的副本,不会改到原数组。

正确修改:

for (int i = 0; i < numbers.length; i++) {
    numbers[i] = numbers[i] * 2;
}

# 5. 多维数组

Java 的多维数组本质是“数组的数组”。

int[][] matrix = new int[2][3];

结构:

matrix
  │
  ▼
┌──────────────┐
│ int[][]      │
│ [0] ─────┐   │
│ [1] ──┐  │   │
└───────┼──┼───┘
        │  │
        ▼  ▼
   ┌──────────┐
   │ int[3]   │  [0, 0, 0]
   └──────────┘
   ┌──────────┐
   │ int[3]   │  [0, 0, 0]
   └──────────┘

二维数组初始化:

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6}
};

Java 支持不规则二维数组:

int[][] triangle = new int[3][];
triangle[0] = new int[] {1};
triangle[1] = new int[] {2, 3};
triangle[2] = new int[] {4, 5, 6};

结构:

triangle[0] -> [1]
triangle[1] -> [2, 3]
triangle[2] -> [4, 5, 6]

遍历二维数组:

for (int row = 0; row < matrix.length; row++) {
    for (int col = 0; col < matrix[row].length; col++) {
        System.out.println(matrix[row][col]);
    }
}

不要默认每一行长度都相同,尤其是数组来自外部输入时。

# 6. 数组复制

常见复制方式:

方法 说明
System.arraycopy 原生复制,性能好
Arrays.copyOf 创建新数组并复制
array.clone() 克隆数组对象
手动循环 可在复制时做转换

System.arraycopy:

int[] source = {1, 2, 3, 4};
int[] target = new int[4];
System.arraycopy(source, 0, target, 0, source.length);

参数含义:

System.arraycopy(
    source,      源数组
    sourcePos,   源数组起始位置
    target,      目标数组
    targetPos,   目标数组起始位置
    length       复制元素个数
)

Arrays.copyOf:

int[] source = {1, 2, 3};
int[] copy = Arrays.copyOf(source, 5);
System.out.println(Arrays.toString(copy)); // [1, 2, 3, 0, 0]

# 6.1 浅拷贝问题

对象数组复制时,复制的是引用,不是对象本身。

User[] users = {new User("Tom")};
User[] copy = Arrays.copyOf(users, users.length);

copy[0].setName("Jerry");
System.out.println(users[0].getName()); // Jerry

内存关系:

users[0] ─────┐
              ▼
           User 对象
              ▲
copy[0]  ─────┘

两个数组指向同一个 User 对象,所以修改对象内容会互相影响。

# 7. Arrays 工具类

java.util.Arrays 提供了大量数组工具方法。

方法 作用
toString 输出一维数组
deepToString 输出多维数组
sort 排序
binarySearch 二分查找
copyOf 扩容或截断复制
fill 填充
equals 比较一维数组内容
deepEquals 比较多维数组内容
asList 转成固定大小 List

示例:

int[] numbers = {3, 1, 2};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers)); // [1, 2, 3]

二分查找前必须有序:

int index = Arrays.binarySearch(numbers, 2);

如果数组无序,二分查找结果没有意义。

多维数组输出:

int[][] matrix = {{1, 2}, {3, 4}};

System.out.println(Arrays.toString(matrix));
System.out.println(Arrays.deepToString(matrix));

Arrays.toString(matrix) 输出的是内层数组引用信息;deepToString 才会递归输出元素。

# 8. 数组与可变参数

可变参数本质上是数组。

public static int sum(int... numbers) {
    int total = 0;
    for (int number : numbers) {
        total += number;
    }
    return total;
}

调用:

sum(1, 2, 3);
sum(new int[] {1, 2, 3});

可变参数规则:

  • 一个方法最多只能有一个可变参数。
  • 可变参数必须放在参数列表最后。
  • 方法内部按照数组使用。

错误写法:

void method(String... names, int count) {
}

正确写法:

void method(int count, String... names) {
}

# 9. 数组与集合的区别

对比项 数组 ArrayList
长度 固定 可动态扩容
元素类型 可存基本类型和引用类型 只能存引用类型
API 较少 丰富
随机访问 O(1) O(1)
插入删除 通常需要移动元素 底层也可能移动元素
泛型支持 不支持泛型数组创建 支持泛型

数组适合长度稳定、性能敏感、元素类型明确的场景。集合适合业务数据数量动态变化、需要丰富操作 API 的场景。

# 10. Arrays.asList 的坑

String[] names = {"Tom", "Jerry"};
List<String> list = Arrays.asList(names);

这个 list 是固定大小列表:

list.add("Alice"); // UnsupportedOperationException

如果需要可变列表:

List<String> list = new ArrayList<>(Arrays.asList(names));

基本类型数组更容易踩坑:

int[] numbers = {1, 2, 3};
List<int[]> list = Arrays.asList(numbers);
System.out.println(list.size()); // 1

因为泛型不能使用基本类型,int[] 会被当作一个整体元素。

# 11. 常见异常

# 11.1 数组越界

int[] numbers = {1, 2, 3};
System.out.println(numbers[3]); // ArrayIndexOutOfBoundsException

合法下标范围是 0 到 length - 1。

# 11.2 空指针

int[] numbers = null;
System.out.println(numbers.length); // NullPointerException

数组引用为 null 时,不能访问 length 或元素。

# 11.3 数组存储异常

数组是协变的:

String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 123; // ArrayStoreException

编译期允许把 String[] 赋值给 Object[],但运行期数组仍然知道自己真实元素类型是 String,因此存入 Integer 会失败。

# Tips 快问快答

Q:数组是基本类型还是引用类型? A:数组是引用类型,数组对象在堆中,数组变量保存引用。

Q:数组长度能改变吗? A:不能。扩容本质是创建新数组,再复制旧数组元素。

Q:array.length 和 String.length() 有什么区别? A:数组的 length 是属性,字符串的 length() 是方法。

Q:为什么数组下标从 0 开始? A:可以把下标理解为相对起始位置的偏移量,第一个元素偏移量是 0。

Q:增强 for 能修改数组元素吗? A:对基本类型不能修改原元素;对引用类型可以通过引用修改对象内部状态,但不能让原数组元素指向新对象。

Q:二维数组一定是矩阵吗? A:不是。Java 二维数组是数组的数组,每一行长度可以不同。

Q:数组复制是深拷贝吗? A:基本类型数组复制值;引用类型数组复制引用,通常是浅拷贝。

Q:Arrays.asList 返回的列表可以新增元素吗? A:不能,它是固定大小列表。如果需要新增元素,应包装成 new ArrayList<>(...)。

Q:为什么 Arrays.asList(new int[]{1,2}) 长度是 1? A:因为 int[] 被当成一个对象元素,而不是被拆成多个 Integer。

Q:数组为什么比很多集合访问快? A:数组结构简单,支持按下标直接定位元素;集合通常在数组之上增加了扩容、泛型、边界处理等逻辑。

上次更新: 2026/06/24, 16:28:28
面向对象编程
String字符串

← 面向对象编程 String字符串→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式