一、数组基本概念
1. 什么是数组
数组是Java中用来存储同类型数据的固定大小的连续内存空间的数据结构。
2. 数组特点
固定长度:一旦创建,长度不可改变
相同类型:所有元素必须是同一数据类型
索引访问:通过下标(从0开始)访问元素
内存连续:元素在内存中是连续存储的
二、数组声明与初始化
1. 数组声明
// 方式1:数据类型[] 数组名; int[] arr1; // 方式2:数据类型 数组名[]; int arr2[];
推荐使用第一种方式,更符合Java规范
2. 数组初始化
(1) 静态初始化
java
int arrB[] = {1,2,3,4,5}
这是 静态初始化的简化形式,只能在 声明数组变量时 直接使用。
编译器会自动推导数组类型和长度。
示例:
java
int arrB[] = {1, 2, 3, 4, 5}; // 正确
arrA = new int[]{1,2,3,4}
这是 静态初始化的完整形式,可以在 声明时或后续赋值时 使用。
需要显式指定数组类型
new int[]
,但长度由元素个数决定。示例:
java
int[] arrA; arrA = new int[]{1, 2, 3, 4}; // 正确(先声明后赋值)
使用场景不同
场景 | int[] arr = {1,2,3} |
arr = new int[]{1,2,3} |
---|---|---|
声明时直接初始化 | ✅ 可用 | ✅ 可用 |
先声明后赋值 | ❌ 不可用 | ✅ 可用 |
方法返回值 | ❌ 不可用 | ✅ 可用 |
匿名数组传参 | ❌ 不可用 | ✅ 可用 |
示例对比:
java
// 1. 声明时初始化(两种方式均可) int[] arr1 = {1, 2, 3}; // 简化形式 int[] arr2 = new int[]{1, 2, 3}; // 完整形式 // 2. 先声明后赋值(只能用完整形式) int[] arr3; arr3 = new int[]{1, 2, 3}; // 正确 // arr3 = {1, 2, 3}; // 错误!简化形式不能用于后续赋值 // 3. 作为方法返回值(只能用完整形式) public int[] getArray() { return new int[]{1, 2, 3}; // 正确 // return {1, 2, 3}; // 错误! } // 4. 匿名数组传参(只能用完整形式) someMethod(new int[]{1, 2, 3}); // 正确 // someMethod({1, 2, 3}); // 错误!
3. 底层实现相同
两种方式最终生成的字节码完全一致,性能无差别。
内存分配方式相同:都在堆内存中创建连续存储的数组对象。
4. 风格建议
推荐使用
int[] arr
声明风格(而非int arr[]
),更符合 Java 规范。如果只是 声明时初始化,优先使用简化形式
{1,2,3}
,代码更简洁。如果需要 重新赋值或匿名使用,必须用完整形式
new int[]{1,2,3}
。
特性 | {1,2,3} |
new int[]{1,2,3} |
---|---|---|
语法名称 | 简化静态初始化 | 完整静态初始化 |
是否依赖声明语句 | 必须与声明写在一起 | 可独立使用 |
灵活性 | 低 | 高 |
推荐使用场景 | 声明时直接初始化 | 重新赋值、方法返回、匿名传参 |
(2) 动态初始化
java
// 指定长度但不指定元素值 int[] arr = new int[5]; // 默认值为0 String[] strs = new String[3]; // 默认值为null
赋值即逐个元素 循环 arraycopy scanner输入赋值都可以
三、数组基本操作
1. 访问数组元素
int[] arr = {10, 20, 30, 40, 50}; // 获取元素 int num = arr[2]; // 获取第3个元素(30) // 修改元素 arr[3] = 100; // 将第4个元素改为100
2. 获取数组长度
int length = arr.length; // 注意不是length()
3. 遍历数组
这里补充循环一定要放在方法中,不能直接在类体内
(1) 普通for循环
for(int i = 0; i < arr.length; i++) { System.out.println(arr[i]); }
(2) 增强for循环
for(int num : arr) { System.out.println(num); }
小练习:
//for each 增强for循环 //只是改变了item,没有改变数组也不可以改变,只能做到使用里面的数 //int a=arrB[0]; a=12; arrB=new int[]{1,2,3,4}; for(int item:arrB){ item=(int)(Math.random()*100); } System.out.println(Arrays.toString(arrB)); //数组随机赋值,然后找出数组最大值 arrB=new int[7]; for(int i=0;i<arrB.length;i++){ arrB[i]=(int)(Math.random()*100); } System.out.println(Arrays.toString(arrB)); int max=arrB[0]; for(int item:arrB){ if(item>max){ max=item; } } System.out.println("数组最大值是:"+max); // 数组求和 arrB=new int[7]; for(int i=0;i<arrB.length;i++){ arrB[i]=(int)(Math.random()*100); } System.out.println(Arrays.toString(arrB)); int sum=0; for(int item:arrB){ sum+=item; } System.out.println(sum); //有10个裁判评分,满分为10分,使用随机数模拟,存入数组中 //从中去掉最大分数和最小分数,剩下分数的平均数就是选手得分 //打印出选手得分 //Math.random();//[0,1) *10 0-0.9.9999.. (int)一刀切只能取到9 应该*11 arrB=new int[10]; for(int i=0;i<arrB.length;i++){ arrB[i]=(int)(Math.random()*11); } System.out.println(Arrays.toString(arrB)); max=arrB[0]; int min=arrB[0]; sum=0; for(int item:arrB){ if(item>max){ max=item; } if(item<min){ min=item; } sum+=item; } int score=(sum-max-min)/8; System.out.println(score);
(3) 使用Arrays.toString()
System.out.println(Arrays.toString(arr));
四、多维数组
1. 二维数组声明与初始化
// 静态初始化 int[][] arr1 = {{1, 2}, {3, 4}, {5, 6}}; // 动态初始化 int[][] arr2 = new int[3][2]; // 3行2列
2. 二维数组遍历
for(int i = 0; i < arr.length; i++) { for(int j = 0; j < arr[i].length; j++) { System.out.print(arr[i][j] + " "); } System.out.println(); }
五、数组常用工具类Arrays
1. 排序
int[] arr = {3, 1, 4, 2, 5}; Arrays.sort(arr); // 升序排序
补充:
1.1 冒泡排序
2. 二分查找
int index = Arrays.binarySearch(arr, 4); // 必须是先排序好的才能用二分
3. 数组比较
boolean isEqual = Arrays.equals(arr1, arr2);
4. 数组填充
Arrays.fill(arr, 0); // 全部填充为0
5. 数组复制
int[] newArr = Arrays.copyOf(arr, arr.length);
6.数组扩容
拷贝数组arraycopy 五个参数
源数组 源数组的起始位置(开始复制的位置) 目标数组 目标数组的起始位置(从哪个位置开始粘贴) 拷贝的长度
//数组扩容 声明一个更大的数组代替旧的数组 int[] arrA=new int[10]; arrA[0]=99;arrA[9]=999; int[] arrTemp=new int[arrA.length<<1]; System.arraycopy(arrA,0,arrTemp,0,arrA.length); //原数组 原数组起始位置(从哪个位置开始粘贴) 目标数组 目标数组起始位置 拷贝的长度 arrA=arrTemp; System.out.println(Arrays.toString(arrA));
六、数组常见问题
1. 数组越界异常
java
int[] arr = new int[3]; System.out.println(arr[3]); // ArrayIndexOutOfBoundsException
2. 空指针异常
java
int[] arr = null; System.out.println(arr[0]); // NullPointerException
3. 数组长度不可变
java
int[] arr = new int[5]; // arr.length = 10; // 错误!数组长度不可变
七、数组与内存
1. 内存分配
数组变量存储在栈内存
数组元素存储在堆内存
2. 内存示意图
text
栈内存 堆内存 arr ----> [0][0][0][0][0]
八、数组应用场景
存储大量同类型数据
实现数据结构(如栈、队列)
矩阵运算
排序和搜索算法实现
九、数组与集合的区别
特性 | 数组 | 集合(ArrayList等) |
---|---|---|
长度 | 固定 | 动态可变 |
类型 | 单一 | 可泛型指定 |
性能 | 高 | 略低 |
功能 | 基础 | 丰富的方法 |
十、Java 8+ 数组新特性
1. 并行排序
Arrays.parallelSort(arr);
2. Stream操作
Arrays.stream(arr) .filter(n -> n > 2) .forEach(System.out::println);
3. 集合转数组
List<String> list = Arrays.asList("A", "B", "C"); String[] arr = list.toArray(new String[0]);
十一、补:无限循环与死循环的区别:
//死循环 无限循环 //while(true){} 没有结束条件 死循环 i=-1; for(;i<0;i--){}//有结束条件,逻辑上条件永远达不到,无限循环
1. 无限循环(Infinite Loop)
定义:
有意创建的、设计上需要永不停止的循环结构
通常有特定的业务用途,是程序逻辑的一部分
特点:
✅ 是有意设计的循环结构
✅ 通常包含循环控制机制(如 break
条件)
✅ 服务于特定业务逻辑(如服务器监听、游戏主循环)
典型应用场景:
java
// 服务器主线程循环 while(true) { Socket client = serverSocket.accept(); // 等待客户端连接 new Thread(new ClientHandler(client)).start(); } // 游戏主循环 while(running) { // running是可控的布尔变量 updateGame(); renderFrame(); }
2. 死循环(Dead Loop)
定义:
意外产生的、本应终止却无法停止的循环
属于程序缺陷(bug),会导致程序卡死或资源耗尽
特点:
❌ 是无意产生的程序错误
❌ 缺少正确的终止条件
❌ 会导致程序异常或系统资源耗尽
常见错误示例:
java
// 错误1:忘记更新循环变量 int i = 0; while(i < 10) { // i永远不会改变 System.out.println("Stuck..."); } // 错误2:错误的终止条件 for(int j=1; j!=10; j+=2) { // 当j=9时,j+2=11,永远不等于10 System.out.println(j); }
关键对比表
特征 | 无限循环 | 死循环 |
---|---|---|
设计意图 | 有意设计 | 意外产生 |
是否可控 | 有控制机制(可退出) | 无法控制 |
业务价值 | 实现特定功能 | 纯属程序缺陷 |
典型结构 | while(true) + 内部break |
缺少变量更新/错误条件 |
是否应避免 | 按需使用 | 必须修复 |