20260716-笔记.md 36 KB

JavaSE 基础 — Day04 笔记

日期:2026-07-16
项目c260716
作者:WanJL


一、多维数组

1. 多维数组概述

Java 中的多维数组本质上是数组的数组

维度 本质 说明
一维数组 元素是数据类型的变量或常量 int[] → 元素是 int
二维数组 元素是一维数组 int[][] → 元素是 int[]
三维数组 元素是二维数组 int[][][] → 元素是 int[][]

核心概念:二维数组的每个元素其实是一维数组的引用地址值

二维数组的定义格式

数组类型[][] 数组名 = new 数组类型[二维数组长度][一维数组长度];
// 示例:3行2列的二维数组
int[][] arr = new int[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();
}
  • arr.length — 二维数组的长度(即行数)
  • arr[i].length — 第 i 行一维数组的长度(即列数)

3. 二维数组的静态初始化

int[][] arr1 = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

4. 不规则的二维数组(每个一维数组长度不同)

二维数组中的一维数组不必长度相同

int[] arr01 = {1, 3};
int[] arr02 = {4, 5, 6};
int[] arr03 = {7, 8, 9, 15};
int[][] arr2 = {arr01, arr02, arr03};  // 存放的是一维数组的引用地址

遍历时同样使用两层循环,每个内层循环以对应一维数组的长度为界:

for (int i = 0; i < arr2.length; i++) {
    for (int j = 0; j < arr2[i].length; j++) {
        System.out.print(arr2[i][j] + " ");
    }
    System.out.println();
}

二、增强 for 循环(foreach)

1. foreach 概述

增强 for 循环(foreach) 是 Java 提供的一种简化数组遍历的语法,作用和普通 for 循环一样,主要用于遍历数组或集合中的所有元素

语法格式

for (数据类型 变量 : 数组名) {
    代码块
}

基本示例

int[] arr = {1, 2, 3, 4, 5, 6, 8};
for (int a : arr) {
    System.out.print(a + " ");
}
// 输出:1 2 3 4 5 6 8

⚠️ 注意:foreach 遍历数组时无法获取索引值,所以只适合遍历全部元素。如果需要操作某个指定索引位置的元素,仍需使用普通 for 循环。


2. foreach 遍历二维数组

int[] arr01 = {1, 3};
int[] arr02 = {4, 5, 6};
int[] arr03 = {7, 8, 9, 15};
int[][] arr2 = {arr01, arr02, arr03};

for (int[] a : arr2) {    // 外层 foreach:遍历二维数组,得到每个一维数组
    for (int a1 : a) {    // 内层 foreach:遍历一维数组,得到每个元素
        System.out.print(a1 + " ");
    }
    System.out.println();
}

外层 int[] a 表示二维数组中的每个元素是一维数组,内层 int a1 表示一维数组中的每个元素是 int


3. for 循环 vs foreach 对比

特性 普通 for 循环 增强 for 循环(foreach)
语法 for(int i=0; i<len; i++) for(int a : arr)
索引访问 支持(通过 i 不支持
适用场景 需要索引、修改元素、部分遍历 只需要遍历全部元素
代码简洁性 较繁琐 更简洁
可读性 一般 更好

三、数组工具类 java.util.Arrays

1. 概述

java.util.Arrays 是 Java 提供的数组工具类,包含大量操作数组的静态方法。

命名特点:如果一个类的类名是名词 + s,大多数情况下这个类就是工具类

使用前提:需要导包

import java.util.Arrays;
// import 是导包的关键字,后面跟着【类全路径名】—— 包名.类名

2. 常用方法

方法 作用 示例
Arrays.sort(arr) 对数组进行升序排序 Arrays.sort(arr);
Arrays.toString(arr) 将数组转换为字符串输出 System.out.println(Arrays.toString(arr));
Arrays.copyOf(arr, newLength) 拷贝数组(扩展或截断,多余补默认值) int[] copy = Arrays.copyOf(arr, 10);
Arrays.copyOfRange(arr, from, to) 拷贝数组的指定范围 [from, to) int[] range = Arrays.copyOfRange(arr, 1, 4);
Arrays.binarySearch(arr, key) 二分查找(数组必须先排序),返回索引值(未找到返回负数) int index = Arrays.binarySearch(arr, 689);

详细示例

int[] arr = {7, 6, 78, 1, 689, 87, 165, 7, 4, 5, 8};

// 排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
// 输出:[1, 4, 5, 6, 7, 7, 8, 78, 87, 165, 689]

// 拷贝(扩展长度,多余补 0)
int[] copy = Arrays.copyOf(arr, 10);
System.out.println(Arrays.toString(copy));

// 拷贝指定范围 [1, 4) — 包含索引1,不包含索引4
int[] range = Arrays.copyOfRange(arr, 1, 4);
System.out.println(Arrays.toString(range));  // 输出:[4, 5, 6]

// 二分查找(需先排序),返回元素在数组中的索引
int index = Arrays.binarySearch(arr, 689);
System.out.println(index);  // 输出:10(排序后 689 在索引 10)

⚠️ Arrays.binarySearch() 要求数组必须先排序,否则结果不确定。


四、冒泡排序(Bubble Sort)

1. 冒泡排序概述

冒泡排序是所有排序算法中最直观、最基础的一种。它的名字来源于排序过程就像水里的泡泡一样,大的元素会逐渐"冒"到数组的末端。

核心思想

重复遍历数组,依次比较相邻的两个元素,如果顺序错误(如从小到大排序时前一个比后一个大)就交换。每轮遍历把当前未排序部分的最大值"冒泡"到正确位置。

算法步骤图解

以数组 {5, 3, 8, 6, 4} 为例,从小到大排序:

第1轮(i=0):将最大值 8 冒泡到最后
  [5, 3, 8, 6, 4] → 比较 5>3 → 交换 → [3, 5, 8, 6, 4]
  [3, 5, 8, 6, 4] → 比较 5<8 → 不交换
  [3, 5, 8, 6, 4] → 比较 8>6 → 交换 → [3, 5, 6, 8, 4]
  [3, 5, 6, 8, 4] → 比较 8>4 → 交换 → [3, 5, 6, 4, 8]  ← 8 到位

第2轮(i=1):将最大值 6 冒泡到倒数第二位
  [3, 5, 6, 4, 8] → 比较 3<5 → 不交换
  [3, 5, 6, 4, 8] → 比较 5<6 → 不交换
  [3, 5, 6, 4, 8] → 比较 6>4 → 交换 → [3, 5, 4, 6, 8]  ← 6 到位

第3轮(i=2):将最大值 5 冒泡到倒数第三位
  [3, 5, 4, 6, 8] → 比较 3<5 → 不交换
  [3, 5, 4, 6, 8] → 比较 5>4 → 交换 → [3, 4, 5, 6, 8]  ← 5 到位

第4轮(i=3):将最大值 4 冒泡到倒数第四位
  [3, 4, 5, 6, 8] → 比较 3<4 → 不交换  ← 4 已在正确位置

排序完成 ✅

2. 基础版冒泡排序

int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};

// 冒泡排序需要两层循环
for (int i = 0; i < arr.length - 1; i++) {
    // 外层循环:控制冒泡的轮数(共 arr.length-1 轮)
    // 内层循环:在未排序区间 [0, arr.length-1-i] 中相邻比较
    // 因为 i 后面的元素已经有序,不需要再比较了
    for (int j = 0; j < arr.length - 1 - i; j++) {
        if (arr[j] > arr[j + 1]) {
            // 交换位置(加减法交换)
            arr[j] = arr[j] + arr[j + 1];
            arr[j + 1] = arr[j] - arr[j + 1];
            arr[j] = arr[j] - arr[j + 1];
        }
    }
}
System.out.println(Arrays.toString(arr));

代码要点: | 部分 | 说明 | |------|------| | 外层循环 i | 控制冒泡的轮数arr.length - 1 轮 | | 内层循环 j | 在未排序区间 相邻比较,范围 [0, arr.length-1-i) | | arr.length-1-i | 每轮结束后,末尾 i 个元素已有序,无需再比较 | | 相邻比较 | arr[j] > arr[j+1] 表示前大后小,需要交换 |

为什么外层循环是 arr.length-1 轮? 因为 n 个元素的数组,最多需要 n-1 轮冒泡就可以完成排序(最后一轮只剩一个元素,无需再排)。

为什么内层循环是 arr.length-1-i 第 i 轮结束后,数组末尾的 i 个元素已经是有序的(每轮冒泡出一个最大值),所以内层只需要比较前 arr.length-1-i 个元素。


3. 优化版冒泡排序

如果某轮冒泡过程中没有发生任何交换,说明数组已经有序,可以提前结束排序

int[] arr2 = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};

for (int i = 0; i < arr2.length - 1; i++) {
    boolean b = false;  // 标记本轮是否有交换
    for (int j = 0; j < arr2.length - 1 - i; j++) {
        if (arr2[j] > arr2[j + 1]) {
            // 交换位置
            arr2[j] = arr2[j] + arr2[j + 1];
            arr2[j + 1] = arr2[j] - arr2[j + 1];
            arr2[j] = arr2[j] - arr2[j + 1];
            b = true;   // 本轮有交换
        }
    }
    // 如果本轮没有发生任何交换,说明数组已经有序,提前结束
    if (b == false) {
        break;
    }
    System.out.println("第" + (i + 1) + "轮冒泡后:" + Arrays.toString(arr2));
}

优化点:引入 boolean 标记变量,如果某一轮没有发生交换,说明数组已有序,直接 break 退出循环。


4. 冒泡排序总结

特性 说明
算法类型 比较排序、交换排序
时间复杂度(平均) O(n²)
时间复杂度(最好) O(n) — 数组已有序(优化版)
时间复杂度(最坏) O(n²) — 数组逆序
空间复杂度 O(1) — 原地排序,不需要额外空间
稳定性 ✅ 稳定(相等元素的相对位置不变)
适用场景 小规模数据排序,教学演示

数据结构与算法初探:数组是最基础的线性结构之一,冒泡排序是最基础的排序算法之一。后续还会学习选择排序、插入排序、快速排序等更多算法。


五、课堂练习详解

练习 01:首尾对称交换数组元素

题目:定义数组 {10, 20, 30, 40, 50},将第 1 个与第 5 个交换、第 2 个与第 4 个交换(首尾对称交换)。

核心思路:使用加减法交换(不借助第三个变量)实现两个位置的元素互换。

int[] arr = {10, 20, 30, 40, 50};

// 首尾交换:第1位和第5位
arr[0] = arr[0] + arr[4];
arr[4] = arr[0] - arr[4];
arr[0] = arr[0] - arr[4];

// 首尾交换:第2位和第4位
arr[1] = arr[1] + arr[3];
arr[3] = arr[1] - arr[3];
arr[1] = arr[1] - arr[3];

变量交换的三种方式

方式 代码 优点 缺点
借助临时变量 tmp = a; a = b; b = tmp; 直观、安全 多用一个变量
加减法交换 a = a + b; b = a - b; a = a - b; 无需额外变量 可能溢出(大数)
异或交换 a = a ^ b; b = a ^ b; a = a ^ b; 无需额外变量 可读性差

练习 02:数组循环右移

题目:定义数组 {1, 2, 3, 4, 5},将数组中的所有元素向右移动 2 个位置,超出数组范围的元素循环移到数组开头。

解题思路

先理解全体向右移动 1 位的过程:

  1. 先把最后一个元素保存起来
  2. 倒数第 2 位 → 倒数第 1 位
  3. 倒数第 3 位 → 倒数第 2 位
  4. ...依此类推...
  5. 第 1 位(索引 0)→ 第 2 位(索引 1)
  6. 把保存起来的最后一个元素 → 第 1 位(索引 0)

执行步骤图解(右移 1 位):

原始: [1, 2, 3, 4, 5]
第1步:保存最后一个元素 last = 5
第2步:从后往前移动 ←
       [1, 2, 3, 4, 4]   // arr[4]=arr[3]
       [1, 2, 3, 3, 4]   // arr[3]=arr[2]
       [1, 2, 2, 3, 4]   // arr[2]=arr[1]
       [1, 1, 2, 3, 4]   // arr[1]=arr[0]
第3步:arr[0] = last
结果: [5, 1, 2, 3, 4]

代码实现

int[] arr = {1, 2, 3, 4, 5};
int k = 2;  // 右移位数

// 如果k大于数组长度,取模(移动6次 = 移动1次)
k = k % arr.length;

// 整体右移k位(循环右移)
for (int i = 0; i < k; i++) {
    // 保存最后一个元素
    int last = arr[arr.length - 1];
    // 从后往前,每个元素向后移动1位
    for (int j = arr.length - 1; j > 0; j--) {
        arr[j] = arr[j - 1];
    }
    // 把最后一个元素放到第0位
    arr[0] = last;
}

关键点

  • 内层循环必须从后往前遍历(j = length-1j > 0),否则前面的值会被覆盖
  • k = k % arr.length 解决 k 大于数组长度时的重复移动问题(如移动 6 次 = 移动 1 次)

练习 03:数组去重

题目:定义数组 {5, 2, 8, 2, 5, 9, 1, 8, 3},去除重复元素,只保留第一次出现的元素,输出去重后的新数组。

核心思路

  1. 第一步:统计不重复元素的个数
  2. 第二步:创建新数组(长度为统计结果)
  3. 第三步:将不重复元素放入新数组

判断元素是否重复的方式:检查当前元素 arr[i] 在它之前是否出现过(即 arr[0] ~ arr[i-1] 中是否有相同的值)。

int[] arr = {5, 2, 8, 2, 5, 9, 1, 8, 3};
int count = 0;  // 不重复元素的个数

// 第一步:统计不重复元素的个数
for (int i = 0; i < arr.length; i++) {
    boolean b = false;  // 标记:先认为当前元素没有重复
    for (int j = 0; j < i; j++) {  // 检查 arr[i] 在它之前是否出现过
        if (arr[i] == arr[j]) {
            b = true;   // 找到重复,标记改为 true
            break;      // 跳出内层循环
        }
    }
    if (b == false) {   // 没有重复,计数
        count++;
    }
}

// 第二步:创建新数组
int[] newArr = new int[count];
int index = 0;  // 新数组的索引

// 第三步:将不重复元素放入新数组
for (int i = 0; i < arr.length; i++) {
    boolean b = false;
    for (int j = 0; j < i; j++) {
        if (arr[i] == arr[j]) {
            b = true;
            break;
        }
    }
    if (b == false) {           // 不重复的元素
        newArr[index] = arr[i]; // 放入新数组
        index++;
    }
}

// 输出新数组
for (int i = 0; i < newArr.length; i++) {
    System.out.print(newArr[i] + " ");
}
// 输出:5 2 8 9 1 3

核心技巧

  • 使用布尔标记 boolean b 来判断元素是否重复
  • 内层循环 j < i 只检查当前元素之前的元素,确保只保留第一次出现的元素
  • 两步走:先统计个数 → 再填充数据(因为数组长度在创建后不可改变)

六、课后作业讲解(7月15日作业)

作业 07:数组元素求和

题目:定义数组 {11, 22, 33, 44, 55},用 for 循环遍历数组,计算所有元素的总和。

知识点:数组遍历、累加求和

int[] arr = {11, 22, 33, 44, 55};
int sum = 0;  // 记录累加和
for (int i = 0; i < arr.length; i++) {
    sum += arr[i];  // 通过 arr[i] 获取每个元素,累加到 sum
}
System.out.println(sum);  // 输出:165

作业 08:找数组最大值

题目:定义数组 {3, 8, 1, 9, 4, 7, 2},找出数组中的最大值。

知识点:数组遍历、打擂法(假设法)

int[] arr = {3, 8, 1, 9, 4, 7, 2};
int max = arr[0];  // 先假设第一个元素是最大值
for (int i = 1; i < arr.length; i++) {
    if (arr[i] > max) {  // 如果有元素比当前最大值还大
        max = arr[i];    // 更新最大值
    }
}
System.out.println(max);  // 输出:9

打擂法核心思路:先假设第一个元素是最大值,然后遍历数组,遇到更大的就更新。


作业 09:数组倒序输出

题目:定义数组 {1, 2, 3, 4, 5},从后往前遍历,将元素倒序输出。

知识点:for 循环逆序遍历

int[] arr = {1, 2, 3, 4, 5};
// 从最大索引开始,递减到 0
for (int i = arr.length - 1; i >= 0; i--) {
    System.out.println(arr[i]);
}
// 输出:
// 5
// 4
// 3
// 2
// 1

arr.length - 1(最后一个元素的索引)开始,到 0(第一个元素的索引)结束,每次递减 1。


作业 10:统计奇偶数个数

题目:定义数组 {12, 7, 9, 24, 15, 8, 31, 46},遍历数组,统计奇数和偶数的个数。

知识点:数组遍历、取模判断、统计计数

int[] arr = {12, 7, 9, 24, 15, 8, 31, 46};
int count = 0;  // 奇数个数
for (int i = 0; i < arr.length; i++) {
    if (arr[i] % 2 != 0) {  // 余2不等于0,说明是奇数
        count++;
    }
}
System.out.println("奇数个数:" + count + ",偶数个数:" + (arr.length - count));
// 输出:奇数个数:4,偶数个数:4

偶数的判定arr[i] % 2 == 0
奇数的判定arr[i] % 2 != 0
偶数个数 = 数组总长度 - 奇数个数


七、随堂练习要点

  1. 多维数组本质:数组的数组,二维数组的元素是一维数组的引用地址
  2. 二维数组定义int[][] arr = new int[3][2];(3 行 2 列)
  3. 二维数组遍历:两层循环嵌套,外层行数 arr.length,内层列数 arr[i].length
  4. 不规则二维数组:每个一维数组长度可以不同
  5. foreach 语法for(数据类型 变量 : 数组名) { },适合遍历全部元素
  6. foreach 局限性:无法获取索引值,无法修改元素,只能读取
  7. Arrays 工具类java.util.Arrays,操作数组的静态方法集合
  8. Arrays.sort(arr):对数组进行升序排序
  9. Arrays.toString(arr):将数组转换为字符串输出
  10. Arrays.copyOf(arr, n):拷贝数组,指定新长度,多余补默认值
  11. Arrays.copyOfRange(arr, from, to):拷贝指定范围 [from, to)
  12. Arrays.binarySearch(arr, key):二分查找(必须先排序)
  13. 冒泡排序:重复遍历数组,相邻元素两两比较,大的往后冒
  14. 冒泡排序-外层循环:控制冒泡轮数,共 arr.length - 1
  15. 冒泡排序-内层循环:在未排序区间 [0, arr.length-1-i] 相邻比较
  16. 冒泡排序-优化:引入 boolean 标记,本轮无交换则提前结束
  17. 变量交换-临时变量法tmp = a; a = b; b = tmp;
  18. 变量交换-加减法a = a + b; b = a - b; a = a - b;
  19. 首尾交换:利用对称性,第 i 位与第 length-1-i 位交换
  20. 循环右移:从后往前逐个移动,保存最后一个元素放到开头
  21. 取模优化k = k % arr.length 解决 k 大于数组长度的问题
  22. 数组去重思路:统计不重复个数 → 创建新数组 → 填充不重复元素
  23. 布尔标记法:使用 boolean 变量标记元素是否重复
  24. 打擂法找最大值:假设第一个是最大值,遇到更大的就更新
  25. 数组求和:遍历数组,累加每个元素
  26. 倒序输出:从 length-10 递减遍历
  27. 奇偶判断n % 2 == 0 偶,n % 2 != 0

八、知识拓展

1. 数组的地址值

当直接打印数组变量时,输出的是数组在堆内存中的地址值

int[] a = new int[10];
System.out.println(a);  // 输出类似:[I@b4c966a

地址值含义:

  • [ — 一维数组
  • I — 元素类型为 int
  • @ — 分隔符
  • b4c966a — 堆内存中的十六进制地址

2. 二维数组的内存结构

二维数组 arr2 = {arr01, arr02, arr03} 在内存中:
┌──────────┐
│ arr2[0]  │────→ arr01: [1, 3]
├──────────┤
│ arr2[1]  │────→ arr02: [4, 5, 6]
├──────────┤
│ arr2[2]  │────→ arr03: [7, 8, 9, 15]
└──────────┘

二维数组的每个元素存储的是一维数组的引用地址,而不是元素本身。

3. 二分查找原理

Arrays.binarySearch() 基于二分查找算法(Binary Search):

  • 前提:数组必须已排序
  • 原理:每次将查找范围缩小一半,与中间元素比较
  • 时间复杂度:O(log n) — 效率远高于线性查找 O(n)

    已排序数组:[1, 4, 5, 6, 7, 7, 8, 78, 87, 165, 689]
    查找目标:689
    
    第1次:比较中间元素 7 → 689 > 7,在右半部分查找
    第2次:比较中间元素 87 → 689 > 87,继续在右半部分查找
    第3次:比较中间元素 165 → 689 > 165,继续在右半部分查找
    第4次:找到 689,返回索引 10
    

4. 数组拷贝的深浅问题

Arrays.copyOf()Arrays.copyOfRange() 执行的是浅拷贝

  • 对于基本数据类型:拷贝的是值,互不影响 ✓
  • 对于引用数据类型:拷贝的是引用地址,新数组和原数组指向同一个对象 ⚠️

九、完整代码汇总

Demo01.java — 多维数组

/**
 * @author WanJl
 * @version 1.0
 * @title Demo01
 * @description 多维数组
 * @create 2026/7/16
 */
public class Demo01 {
    public static void main(String[] args) {
        /*
            一维数组中所有的元素都是数据类型的变量或常量
            二维数组中所有的元素都是一维数组
            三维数组中所有的元素都是二维数组
            ......

            Java多维数组的本质:其实就是数组的数组,二维数组的元素其实就是一维数组的引用地址值

            二维数组的定义的格式:
            数组类型[][] 数组名=new 数组类型[二维数组长度][一维数组长度];
         */
        //数组类型[][] 数组名=new 数组类型[二维数组长度][一维数组长度];
        int[][] arr = new int[3][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();
        }
        // 静态初始化二维数组----三行三列
        int[][] arr1 = {
                {1, 2, 3},
                {4, 5, 6},
                {7, 8, 9}
        };
        for (int i = 0; i < arr1.length; i++) {
            for (int j = 0; j < arr1[i].length; j++) {
                System.out.print(arr1[i][j] + " ");
            }
            System.out.println();
        }

        // 二维数组中的一维数组,不是必须要相同长度的,长度是不一样的。
        int[] arr01 = {1, 3};
        int[] arr02 = {4, 5, 6};
        int[] arr03 = {7, 8, 9, 15};
        int[][] arr2 = {arr01, arr02, arr03}; //二维数组存放的其实就是一维数组的引用地址
        for (int i = 0; i < arr2.length; i++) {
            for (int j = 0; j < arr2[i].length; j++) {
                System.out.print(arr2[i][j] + " ");
            }
            System.out.println();
        }
    }
}

Demo02.java — foreach 增强 for 循环

/**
 * @author WanJl
 * @version 1.0
 * @title Demo02
 * @description foreach迭代-增强for循环
 * @create 2026/7/16
 */
public class Demo02 {
    public static void main(String[] args) {
        //我们之前使用的for循环,是for(int i=0;i<10;i++)
        //可以使用增强for循环--foreach,作用和for循环一样
        //主要是用来进行数组遍历
        /*
            格式:
                for(数据类型 变量 : 数组名){
                    代码块
                }
         */
        int[] arr = {1, 2, 3, 4, 5, 6, 8};
        for (int a : arr) {
            System.out.print(a + " ");
        }
        System.out.println();
        /*
            foreach 遍历的数组,无法获取索引值,所以只适合遍历全部元素。想要获取某个索引位置的元素,需要for循环
         */

        int[] arr01 = {1, 3};
        int[] arr02 = {4, 5, 6};
        int[] arr03 = {7, 8, 9, 15};
        int[][] arr2 = {arr01, arr02, arr03};
        for (int[] a : arr2) { //外层foreach
            for (int a1 : a) { //内层foreach
                System.out.print(a1 + " ");
            }
            System.out.println();
        }
    }
}

Demo03.java — Arrays 数组工具类

import java.util.Arrays;

/**
 * @author WanJl
 * @version 1.0
 * @title Demo03
 * @description 数组工具类 : java.util.Arrays;
 * @create 2026/7/16
 */
public class Demo03 {
    public static void main(String[] args) {
        /*
            数组工具类 : java.util.Arrays;
            java中的命名特点,如果一个类的类名 是名词+s,那么大多数情况下,这个类就是一个工具类
         */
        int[] arr = {7, 6, 78, 1, 689, 87, 165, 7, 4, 5, 8};
        //使用工具类对数组进行排序
        Arrays.sort(arr);

        //输出arr数组--Arrays.toString方法 是直接把arr数组转换成字符串
        System.out.println(Arrays.toString(arr));
        // 拷贝(扩展长度,多余补 0)-- 把一个数组的元素复制到一个新的数组并指定新数组的长度,多余补 0
        int[] copy = Arrays.copyOf(arr, 10);
        System.out.println(Arrays.toString(copy));
        // 拷贝指定范围 [1,4)
        int[] range = Arrays.copyOfRange(arr, 1, 4);
        System.out.println(Arrays.toString(range));
        // 二分查找(需先排序),返回索引值
        int index = Arrays.binarySearch(arr, 689);
        System.out.println(index);
    }
}

Demo04.java — 冒泡排序

import java.util.Arrays;

/**
 * @author WanJl
 * @version 1.0
 * @title Demo04
 * @description 数组排序-冒泡排序
 * @create 2026/7/16
 */
public class Demo04 {
    /*
        我们现在已经学了最基础的  数据结构与算法
        数据结构:数组结构--属于线性结构的一种
        算法:比如排序算法(冒泡、选择、插入...)
        冒泡排序,是所有排序【算法】最直观的,最基础的一种。
        冒泡排序就像水里的泡泡一样,大的元素会逐渐的冒到数组的末端。

        冒泡排序的核心思想:
            重复遍历数组,依次比较相邻的两个元素,如果顺序错误就交换。
            每轮遍历把当前未排序的部分的最大值"冒泡"到正确位置。
     */
    public static void main(String[] args) {
        /*
            冒泡排序的基础版
            每一轮把未排序部分的最大值"冒泡"到数组末尾
         */
        int[] arr = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
        //冒泡排序需要两层循环
        for (int i = 0; i < arr.length - 1; i++) {  //外层循环,控制的一共冒泡多少轮
            //内层循环 在未排序的区间[0,arr.length-1-i] 中相邻比较
            //因为 i 后面的元素已经有序了,不需要再比较了
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    //交换位置(加减法交换)
                    arr[j] = arr[j] + arr[j + 1];
                    arr[j + 1] = arr[j] - arr[j + 1];
                    arr[j] = arr[j] - arr[j + 1];
                }
            }
        }
        System.out.println(Arrays.toString(arr));

        // 优化版
        int[] arr2 = {1, 456, 782, 5, 8, 4, 598, 1, 46, 68, 465, 74};
        for (int i = 0; i < arr2.length - 1; i++) {
            boolean b = false;    //定义boolean类型的变量,用来标记本轮是否有交换
            for (int j = 0; j < arr2.length - 1 - i; j++) {
                if (arr2[j] > arr2[j + 1]) {
                    //交换位置
                    arr2[j] = arr2[j] + arr2[j + 1];
                    arr2[j + 1] = arr2[j] - arr2[j + 1];
                    arr2[j] = arr2[j] - arr2[j + 1];
                    b = true; //本轮有交换
                }
            }
            //判断,如果本轮没有发生任何交换,说明数组已经有序,提前结束
            if (b == false) {
                break;
            }
            System.out.println("第" + (i + 1) + "轮冒泡后:" + Arrays.toString(arr2));
        }
    }
}

Exercises01.java — 首尾对称交换

/**
 * @author WanJl
 * @version 1.0
 * @title Exercises01
 * @description 课堂练习01:首尾对称交换数组元素
 * @create 2026/7/16
 */
public class Exercises01 {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30, 40, 50};
        System.out.print("交换前:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
        System.out.print("交换后:");
        //首尾交换 第1位和第5位,第2位和第4位
        arr[0] = arr[0] + arr[4];
        arr[4] = arr[0] - arr[4];
        arr[0] = arr[0] - arr[4];

        arr[1] = arr[1] + arr[3];
        arr[3] = arr[1] - arr[3];
        arr[1] = arr[1] - arr[3];

        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

Exercises02.java — 数组循环右移

/**
 * @author WanJl
 * @version 1.0
 * @title Exercises02
 * @description 课堂练习02:数组循环右移
 * @create 2026/7/16
 */
public class Exercises02 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int k = 2; //右移位数
        //如果k大于数组长度,要取模
        k = k % arr.length;

        System.out.print("移动前:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
        //整体右移k位(循环右移)
        for (int i = 0; i < k; i++) {
            //保存最后一个元素
            int last = arr[arr.length - 1];
            //从后往前,每个元素向后移动1位
            for (int j = arr.length - 1; j > 0; j--) {
                arr[j] = arr[j - 1];
            }
            //把最后1位给第0位
            arr[0] = last;
        }
        System.out.print("移动后:");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

Exercises03.java — 数组去重

/**
 * @author WanJl
 * @version 1.0
 * @title Exercises03
 * @description 课堂练习03:数组去重
 * @create 2026/7/16
 */
public class Exercises03 {
    public static void main(String[] args) {
        /*
            先统计不重复元素的个数
         */
        int[] arr = {5, 2, 8, 2, 5, 9, 1, 8, 3};
        int count = 0;    //个数
        for (int i = 0; i < arr.length; i++) {
            boolean b = false;    //设置false,表示先认为当前的数组元素是没有重复的
            for (int j = 0; j < i; j++) {    //找 arr[i]元素,在arr数组中,有没有重复的元素。
                if (arr[i] == arr[j]) {    //只要找到一个重复的
                    b = true; //就把b设置为 true
                    break;  //跳出内层循环
                }
            }
            if (b == false) {  //那就说明这个元素是没有重复的,就计数
                count++;
            }
        }
        //创建的新的数组长度就是 count
        int[] newArr = new int[count];    //动态初始化
        int index = 0;    //作为新数组的索引值
        for (int i = 0; i < arr.length; i++) {
            boolean b = false;    //设置false,表示先认为当前的数组元素是没有重复的
            //检查arr[i]在它之前是否有出现过
            for (int j = 0; j < i; j++) {
                if (arr[i] == arr[j]) {
                    b = true;
                    break;
                }
            }
            //判断如果我们设置的标记还是false,说明没有重复
            if (b == false) {
                newArr[index] = arr[i];
                index++;
            }
        }
        for (int i = 0; i < newArr.length; i++) {
            System.out.print(newArr[i] + " ");
        }
    }
}

HomeWork07.java — 数组求和

/**
 * @author WanJl
 * @version 1.0
 * @title HomeWork07
 * @description 7月15日作业:数组元素求和
 * @create 2026/7/16
 */
public class HomeWork07 {
    public static void main(String[] args) {
        int[] arr = {11, 22, 33, 44, 55};
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        System.out.println(sum);
    }
}

HomeWork08.java — 找数组最大值

/**
 * @author WanJl
 * @version 1.0
 * @title HomeWork08
 * @description 7月15日作业:找数组最大值
 * @create 2026/7/16
 */
public class HomeWork08 {
    public static void main(String[] args) {
        int[] arr = {3, 8, 1, 9, 4, 7, 2};
        int max = arr[0];  //假设第一个元素是最大值
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        System.out.println(max);
    }
}

HomeWork09.java — 数组倒序输出

/**
 * @author WanJl
 * @version 1.0
 * @title HomeWork09
 * @description 7月15日作业:数组倒序输出
 * @create 2026/7/16
 */
public class HomeWork09 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        for (int i = arr.length - 1; i >= 0; i--) {
            System.out.println(arr[i]);
        }
    }
}

HomeWork10.java — 统计奇偶数个数

/**
 * @author WanJl
 * @version 1.0
 * @title HomeWork10
 * @description 7月15日作业:统计奇偶数个数
 * @create 2026/7/16
 */
public class HomeWork10 {
    public static void main(String[] args) {
        int[] arr = {12, 7, 9, 24, 15, 8, 31, 46};
        int count = 0;    //奇数个数
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] % 2 != 0) {
                count++;
            }
        }
        System.out.println("奇数个数:" + count + ",偶数个数:" + (arr.length - count));
    }
}

十、拓展阅读

概念 说明
多维数组 Java 中多维数组是数组的数组,二维数组的元素是一维数组的引用地址
二维数组遍历 外层循环遍历行(arr.length),内层循环遍历列(arr[i].length
不规则数组 二维数组中的每个一维数组长度可以不同
增强 for 循环 for(类型 变量 : 数组) 语法,简化数组/集合遍历,无法获取索引
Arrays 工具类 java.util.Arrays 包含 sort、toString、copyOf、binarySearch 等静态方法
Arrays.sort() 对数组进行快速排序(Dual-Pivot Quicksort)
Arrays.toString() 将数组转换为可读的字符串格式 [elem1, elem2, ...]
Arrays.copyOf() 数组拷贝,可指定新长度,扩展部分补默认值,截断部分丢弃
Arrays.copyOfRange() 拷贝数组指定范围 [from, to),包含 from 不包含 to
Arrays.binarySearch() 二分查找,数组必须先排序,返回索引值(未找到返回负数)
冒泡排序 重复遍历数组,相邻元素两两比较,大的往后冒,每轮把最大值冒泡到末尾
冒泡排序-外层循环 控制冒泡轮数,n 个元素最多需要 n-1 轮
冒泡排序-内层循环 在未排序区间 [0, arr.length-1-i] 相邻比较,比较范围随轮数递减
冒泡排序-优化 引入 boolean 标记判断本轮是否有交换,无交换则提前结束排序
冒泡排序-时间复杂度 平均 O(n²),最好 O(n)(优化版已有序),最坏 O(n²)
冒泡排序-空间复杂度 O(1),原地排序,不需要额外空间
变量交换-临时变量 tmp = a; a = b; b = tmp; 最直观的交换方式
变量交换-加减法 a = a + b; b = a - b; a = a - b; 不借助第三个变量
数组循环右移 从后往前逐位移动,保存最后一位到第一位,外层套循环控制移动次数
数组去重 两层循环 + 布尔标记,检查当前元素在它之前是否出现过
打擂法 先假设第一个是最大值/最小值,遍历数组遇到更值就更新
数组求和 sum += arr[i] 遍历累加
倒序遍历 for (int i = length-1; i >= 0; i--)
奇偶判断 n % 2 == 0 为偶数,n % 2 != 0 为奇数
取模优化 对循环移动的位数取模,避免无效重复移动