数组(Java基础语法)
数组的声明
语法拆解:int[] scores;
我们把这句代码分成三个部分来看:
1. int (元素类型 - Type)
含义: 这部分指定了未来这个数组中,每一个“格子”里存放的数据的类型。
int 表示这个数组是一个“整型数组”,它里面的每个元素都必须是 int 类型的整数。
你也可以声明其他类型的数组:
double[] prices; (一个用来装 double 类型价格的数组)
String[] names; (一个用来装 String 类型名字的数组)
char[] letters; (一个用来装 char 类型字母的数组)
规则: 一个数组一旦被声明为某种类型,它就永远只能存放该类型的数据,不能混装。
2. [] (数组标识符)
含义: 这对方括号 [] 是语法的核心,它告诉编译器:“这不是一个普通的 int 变量,而是一个能装很多个 int 的数组!”
它就像给变量贴上了一个“容器”的标签。
int scores; -> 只能装 一个 int。
int[] scores; -> 能装 一串 int。
写法的灵活性:
int[] scores; ( 推荐的写法,类型 int[] 和变量名 scores 分离,更清晰)
int scores[]; ( C/C++风格的写法,Java为了兼容也支持,但不推荐)
这两种写法在功能上完全等价,但社区和官方更推荐第一种,因为它更符合Java的“类型 变量名”的声明风格。
3. scores (变量名 - Identifier)
含义: 这是我们给这个“数组引用变量”起的名字。
以后,我们就可以通过 scores 这个名字,来访问和操作这个数组了。
变量名需要遵循Java的标识符命名规范(比如不能以数字开头,不能是关键字等),通常使用有意义的小驼峰命名法
数组的初始化
哪种情况用哪个?
初始化方式 | 决策依据 | 核心思想 | 例子 |
动态初始化 | 只知道长度,不知道内容 | “先占坑,后填数” | 用户输入、存放计算结果 |
静态初始化 | 已经知道确切内容 | “坑和数,一步到位” | 星期、月份、固定配置、测试数据 |
1. 静态初始化 (Static Initialization)
“内容驱动”的初始化
核心思想: 在创建数组的同时,就直接指定数组中要存放的具体元素内容。数组的长度由你提供元素的个数来自动决定。
适用场景: 当你已经明确知道数组里应该放哪些具体的值时,就用静态初始化。
口诀: “内容决定长度”。
完整语法:
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};
示例:
int[] arr = new int[]{1, 2, 3, 4, 5};
String[] names = new String[]{"张三", "李四", "王五"};
简化语法 (更常用):
数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};
2. 动态初始化 (Dynamic Initialization)
“长度驱动”的初始化
核心思想: 在创建数组时,只指定数组的长度,而不关心里面元素的具体内容。数组中每个元素的初始值,会由系统根据其数据类型自动分配一个默认值。
适用场景: 当你只知道需要多大的空间,但具体的值要在程序运行过程中,再慢慢填充进去时,就用动态初始化。
口诀: “长度决定内容”。
语法:
数据类型[] 数组名 = new 数据类型[数组长度];
示例:
// 创建一个能存放5个整数的数组,所有位置的初始值都是 0
int[] numbers = new int[5]; // 创建一个能存放10个字符串的数组,所有位置的初始值都是 null
String[] userList = new String[10];
动态初始化之后,数组里填的都是默认值(int 就是 0)。给它填上我们想要的数,其实非常简单,核心就一步:通过索引定位,然后赋值。
方式一:手动一个一个地填(最基础,但不常用)
这就像你手动操作:
找到第1个格子(索引是0),把里面的 0 换成 10。
找到第2个格子(索引是1),把里面的 0 换成 20。
找到第3个格子(索引是2),把里面的 0 换成 30。
对应的代码:
int[] array2 = new int[3]; // 此时内容是 {0, 0, 0}// 开始填数
array2[0] = 10; // 把 10 赋值给索引为 0 的位置
array2[1] = 20; // 把 20 赋值给索引为 1 的位置
array2[2] = 30; // 把 30 赋值给索引为 2 的位置// 现在,array2 的内容就变成了 {10, 20, 30}
System.out.println(array2[0]); // 输出 10
System.out.println(array2[1]); // 输出 20
System.out.println(array2[2]); // 输出 30
缺点:如果数组有100个元素,你总不能写100行代码吧?太笨拙了!所以我们需要更聪明的方法。
方式二:使用 for 循环来填(最常用,最高效)
这才是我们处理数组的标准方法。循环可以自动地、重复地帮我们完成“定位 -> 赋值”这个操作。
核心思路:
让循环变量 i 从 0 开始,每次循环都作为数组的索引,直到遍历完所有位置。
一个完整的例子:从键盘输入5个学生的成绩
import java.util.Scanner;public class FillArrayExample {public static void main(String[] args) {// 1. 动态初始化:创建一个能装5个分数的数组,现在全是0int[] scores = new int[5];Scanner sc = new Scanner(System.in);System.out.println("请输入5位学生的成绩:");// 2. 使用 for 循环填数// scores.length 的值是 5// 循环会执行5次,i 的值会依次是 0, 1, 2, 3, 4for (int i = 0; i < scores.length; i++) {System.out.print("请输入第 " + (i + 1) + " 位学生的成绩: ");// 关键代码在这里!// 第一次循环 (i=0): scores[0] = 用户输入的值// 第二次循环 (i=1): scores[1] = 用户输入的值// ...以此类推scores[i] = sc.nextInt(); }System.out.println("--------------------");System.out.println("所有学生的成绩录入完毕,如下:");// 3. 使用另一个 for 循环来查看结果for (int i = 0; i < scores.length; i++) {System.out.println("第 " + (i + 1) + " 位学生的成绩是: " + scores[i]);}}
}
运行这个程序,你会看到:
请输入5位学生的成绩:
请输入第 1 位学生的成绩: 98
请输入第 2 位学生的成绩: 85
请输入第 3 位学生的成绩: 77
请输入第 4 位学生的成绩: 92
请输入第 5 位学生的成绩: 100
--------------------
所有学生的成绩录入完毕,如下:
第 1 位学生的成绩是: 98
第 2 位学生的成绩是: 85
第 3 位学生的成绩是: 77
第 4 位学生的成绩是: 92
第 5 位学生的成绩是: 100
数组的访问
“访问”数组,主要包含两个动作:
读取数组中某个位置的元素值。
修改数组中某个位置的元素值。
这两个动作都依赖于同一个关键概念——索引 (Index)。
1. 核心概念:索引 (Index)
你可以把数组想象成电影院里的一排座位,或者一栋楼里的一个个房间。
数组: 就是那一整排座位。
元素: 就是每个座位上坐着的人。
索引: 就是每个座位的编号。
索引有两条铁律,必须牢记:
索引从 0 开始:
第一个元素的索引是 0,第二个是 1,第三个是 2,以此类推。
这是一个非常非常重要的计算机编程约定,几乎所有主流语言都遵循这个规则。
最大索引是 数组长度 - 1:
如果一个数组有 5 个元素(长度为5),那么它的索引范围就是 0, 1, 2, 3, 4。最大索引是 5 - 1 = 4。
如果你试图访问超出这个范围的索引(比如 -1 或者 5),程序就会立刻崩溃,并抛出著名的 ArrayIndexOutOfBoundsException (数组索引越界异常)。
2. 访问数组的语法
访问数组的语法非常简单,就是:
数组名[索引]
这个语法既可以用来读取,也可以用来修改。
代码示例:
我们先创建一个数组,作为操作的对象:
// 创建一个长度为 5 的整型数组,用来存放考试成绩
int[] scores = {95, 88, 76, 99, 82};
现在,这个 scores 数组在内存中的样子是:
元素值 | 95 | 88 | 76 | 99 | 82 |
索引 | 0 | 1 | 2 | 3 | 4 |
3. 读取元素 (Reading Elements)
把 数组名[索引] 这个表达式放在等号的右边,或者直接用在打印语句里,就是读取操作。
public class ArrayRead {public static void main(String[] args) {int[] scores = {95, 88, 76, 99, 82};// 读取第一个元素 (索引为 0)int firstScore = scores[0];System.out.println("第一个学生的成绩是: " + firstScore); // 输出: 95// 读取第三个元素 (索引为 2)int thirdScore = scores[2];System.out.println("第三个学生的成绩是: " + thirdScore); // 输出: 76// 读取最后一个元素 (索引为 长度-1)// scores.length 的值是 5int lastScore = scores[scores.length - 1]; // scores[4]System.out.println("最后一个学生的成绩是: " + lastScore); // 输出: 82// 错误示例:访问不存在的索引// int errorScore = scores[5]; // 这行代码会立刻让程序崩溃!}
}
4. 修改元素 (Modifying Elements)
把 数组名[索引] 这个表达式放在等号的左边,就是修改操作(也叫赋值)。
public class ArrayModify {public static void main(String[] args) {int[] scores = {95, 88, 76, 99, 82};// 打印修改前的第二个成绩System.out.println("修改前,第二个学生的成绩是: " + scores[1]); // 输出: 88// 修改第二个元素的值 (索引为 1)// 把 90 这个值,存放到索引为 1 的位置上scores[1] = 90;// 打印修改后的第二个成绩System.out.println("修改后,第二个学生的成绩是: " + scores[1]); // 输出: 90// --- --- ---// 假设第四个学生 (索引为 3) 的成绩录错了,应该是满分scores[3] = 100;System.out.println("订正后,第四个学生的成绩是: " + scores[3]); // 输出: 100}
}
数组的属性
length 是数组一个非常重要的内置属性,它简单、直接,但又极其有用。
1. 核心概念:length 是什么?
定义: length 是每个数组对象都自带的一个属性,它表示这个数组可以容纳的元素总数。
直观理解: 它就是数组的“长度”或者“容量”。
语法: 数组名.length
返回值: 一个 int 类型的整数。
重要特性:
它是属性,不是方法: 注意 length 后面没有括号 ()。
scores.length (✔︎ 正确)
scores.length() (❌ 错误! 这会让编译器以为你在调用一个叫 length 的方法)
(这与字符串的 length() 方法不同,是初学者容易混淆的地方)
它是 final 的 (只读): 一旦数组被创建,它的长度就固定不变了。你只能读取 length 属性的值,不能修改它。
int len = scores.length; (✔︎ 正确)
scores.length = 10; (❌ 错误! 无法为最终变量length分配值)
2. length 的作用和用法
length 属性最核心的用途,就是让我们的代码能够“知道”数组有多长,从而进行安全、灵活的操作。它主要用在以下两个关键场景:
场景一:获取数组的长度信息
这是最直接的用法。
代码示例:
codeJava
public class ArrayLengthDemo {public static void main(String[] args) {// 静态初始化的数组String[] fruits = {"Apple", "Banana", "Orange"};int fruitCount = fruits.length;System.out.println("水果数组的长度是: " + fruitCount); // 输出: 3// 动态初始化的数组int[] numbers = new int[10]; // 创建一个长度为10的数组int numberCapacity = numbers.length;System.out.println("数字数组的长度是: " + numberCapacity); // 输出: 10// 空数组double[] emptyArray = {};System.out.println("空数组的长度是: " + emptyArray.length); // 输出: 0}
}
场景二:数组的遍历 (最重要的用途!)
遍历 (Traversal),就是从头到尾访问数组中的每一个元素。如果我们不知道数组有多长,就无法做到这一点。length 属性是实现数组遍历的关键。
问题: 如何打印出 scores 数组中的每一个成绩?
错误的方式 (硬编码):
int[] scores = {95, 88, 76, 99, 82};
System.out.println(scores[0]);
System.out.println(scores[1]);
System.out.println(scores[2]);
System.out.println(scores[3]);
System.out.println(scores[4]);
这种写法非常笨拙,如果数组有100个元素,你就要写100行。而且如果数组长度变了,代码就得重写。
正确的方式 (使用 for 循环和 .length):
public class ArrayTraversal {public static void main(String[] args) {int[] scores = {95, 88, 76, 99, 82, 100, 75}; // 我们可以随时增删元素System.out.println("数组 scores 的长度是: " + scores.length); // 输出: 7// 使用 for 循环遍历数组// i 的范围是从 0 到 scores.length - 1for (int i = 0; i < scores.length; i++) {// 在循环中,i 的值会依次是 0, 1, 2, 3, 4, 5, 6System.out.println("索引为 " + i + " 的元素是: " + scores[i]);}}
}
输出:
数组 scores 的长度是: 7
索引为 0 的元素是: 95
索引为 1 的元素是: 88
索引为 2 的元素是: 76
索引为 3 的元素是: 99
索引为 4 的元素是: 82
索引为 5 的元素是: 100
索引为 6 的元素是: 75
为什么这种方式好?
自动化: 循环会自动处理从第一个到最后一个的所有元素。
灵活性/自适应: 无论 scores 数组的长度变成多少(5个、10个、100个),这段遍历代码完全不需要修改,因为它总是通过 scores.length 来动态获取正确的循环次数。这让我们的代码非常健壮。
安全性: i < scores.length 这个条件,完美地保证了索引 i 的最大值只会是 scores.length - 1,从而杜绝了数组越界的风险。