数组 vs. 对象
要明确一个关键点:Java 中的数组本身就是一个对象。
当你创建一个 String[](字符串数组)时,你是在创建一个数组对象,这个数组对象的唯一作用是存放其他 String 对象的引用。
这个过程可以分为两步:
- 创建数组对象:为数组分配内存空间,用于存放固定数量的元素(10个),数组中的每个“位置”(槽位)都存放着一个默认值,对于对象引用类型,默认值是
null。 - 创建并赋值字符串对象:将具体的
String对象创建出来,并将其引用赋值给数组中的某个槽位。
声明并初始化(最常用)
这是最直接、最常见的方式,在声明数组的同时就创建并初始化它。
使用字面量(简洁方式)
这种方式最简单,适用于你已经知道数组中所有元素的情况。
// 语法:类型[] 数组名 = {元素1, 元素2, ...};
String[] names = {"Alice", "Bob", "Charlie"};
// 访问数组元素
System.out.println("第一个名字是: " + names[0]); // 输出: 第一个名字是: Alice
System.out.println("数组的长度是: " + names.length); // 输出: 数组的长度是: 3
内部发生了什么?
- JVM 创建了一个包含 3 个槽位的
String[]数组对象。 - JVM 为
"Alice","Bob","Charlie"这三个字符串字面量创建String对象(Java 会对字符串字面量进行优化,可能不会重复创建)。 - 将这些
String对象的引用分别存入数组的 0, 1, 2 号槽位。
使用 new 关键字(显式方式)
这种方式更清晰地展示了数组的创建过程,适用于动态确定元素或分步初始化的情况。
// 语法:类型[] 数组名 = new 类型[长度];
// 先创建一个长度为 3 的 String 数组,所有元素默认为 null
String[] fruits = new String[3];
// 然后分别为每个元素赋值
fruits[0] = "Apple";
fruits[1] = "Banana";
fruits[2] = "Orange";
// 访问数组元素
System.out.println("第二种水果是: " + fruits[1]); // 输出: 第二种水果是: Banana
内部发生了什么?
new String[3]这行代码创建了一个包含 3 个槽位的String[]数组对象。fruits[0],fruits[1],fruits[2]的值都是null。- 后续的赋值语句(如
fruits[0] = "Apple";)会创建"Apple"这个String对象,并将其引用存入fruits[0]。
先声明,后初始化
如果你需要在代码的不同部分创建和填充数组,可以先声明数组变量,然后再进行初始化。
// 第一步:声明一个 String 类型的数组变量
// 数组变量 namesRef 还没有指向任何实际的数组对象,它只是个引用
String[] namesRef;
// 第二步:创建一个数组对象并赋值给 namesRef
// 可以使用字面量方式
namesRef = new String[]{"David", "Eve", "Frank"};
// 也可以使用 new 关键字分步赋值
String[] citiesRef;
citiesRef = new String[2];
citiesRef[0] = "New York";
citiesRef[1] = "London";
// 访问
System.out.println("城市列表的第一个元素: " + citiesRef[0]); // 输出: 城市列表的第一个元素: New York
使用 Arrays 工具类
java.util.Arrays 类提供了一些静态方法来方便地操作数组,Arrays.asList() 可以将一个数组转换为一个 List 集合。
import java.util.Arrays;
import java.util.List;
String[] colors = {"Red", "Green", "Blue"};
// 使用 Arrays.asList() 将数组转换为 List
// 注意:返回的 List 是一个固定大小的视图列表,不能添加或删除元素
List<String> colorList = Arrays.asList(colors);
System.out.println("转换后的 List: " + colorList); // 输出: 转换后的 List: [Red, Green, Blue]
// 可以像操作 List 一样访问元素
System.out.println("List 中的第二个颜色: " + colorList.get(1)); // 输出: List 中的第二个颜色: Green
重要注意事项
数组的长度是固定的
一旦数组被创建,其长度就不能再改变,如果你需要一个可以动态改变大小的集合,应该使用 ArrayList。
// 错误示范:编译会失败 String[] dynamicArray = new String[3]; // dynamicArray[3] = "New Element"; // 抛出 ArrayIndexOutOfBoundsException 异常
NullPointerException (空指针异常)
这是一个非常常见的错误,当你尝试访问一个值为 null 的数组元素时,就会抛出此异常。
String[] items = new String[3]; // items[0], items[1], items[2] 此时都是 null // 下面这行代码会抛出 NullPointerException // System.out.println(items[0].length());
ArrayIndexOutOfBoundsException (数组下标越界异常
当你访问一个不存在的索引时(负数或大于等于 length 的值),就会抛出此异常。
String[] names = {"Tom", "Jerry"};
// names[2] 不存在,因为索引范围是 0 到 1
// 下面这行代码会抛出 ArrayIndexOutOfBoundsException
// System.out.println(names[2]);
数组的循环遍历
遍历数组是基本操作,有几种常见方式:
String[] languages = {"Java", "Python", "C++"};
// 1. 经典的 for 循环(通过索引)
System.out.println("--- 使用 for 循环 ---");
for (int i = 0; i < languages.length; i++) {
System.out.println("语言: " + languages[i]);
}
// 2. 增强的 for 循环 (for-each loop) - 更简洁,推荐使用
System.out.println("\n--- 使用 for-each 循环 ---");
for (String lang : languages) {
System.out.println("语言: " + lang);
}
总结与最佳实践
| 方法 | 示例 | 适用场景 |
|---|---|---|
| 字面量初始化 | String[] arr = {"A", "B"}; |
最常用,当你已经知道所有元素时,代码最简洁。 |
new 关键字初始化 |
String[] arr = new String[2]; arr[0] = "A"; |
当你需要先创建数组,然后在后续代码中动态填充元素时。 |
| 先声明后初始化 | String[] arr; arr = new String[]{"A", "B"}; |
当数组的创建和初始化逻辑需要分开时。 |
Arrays.asList() |
List<String> list = Arrays.asList(arr); |
当你需要将数组作为 List 集合来使用时。 |
对于初学者和绝大多数日常开发场景,强烈推荐使用第一种方法(字面量初始化),因为它最直观、最不容易出错,只有在需要动态确定数组长度或分步填充时,才考虑使用 new 关键字的方式。
