第7章 数组处理大量同类型数据时逐个定义变量是不现实的。数组是 Java 中存储一组同类型数据的容器。本节学习数组的定义、遍历和常见操作。一、什么是数组数组是相同类型元素的集合在内存中是一段连续的空间。int[]scores{85,92,78,65,99};特点元素类型必须一致长度固定一旦创建不可改变通过**下标索引**访问元素下标从 0 开始是引用类型为什么需要数组统计一个班的成绩不用数组要写 5 个变量、求平均分要写 5 遍用数组一个变量就能装下还能循环统一处理int[]scores{85,92,78,65,99};// 成绩加到 100 个这里也只改一处intsum0;for(ints:scores){sums;}System.out.println(平均分(double)sum/scores.length);// 83.8为什么下标从 0 开始下标本质是相对于数组起始位置的偏移量第 1 个元素偏移 0 格第 2 个偏移 1 格和数组在内存中连续存放的方式一致。说明本系列统一按 JDK 8 / JDK 11 / JDK 17 三个长期支持版本来讲。数组的语法从 JDK 8 到 JDK 17 几乎没有变化这一节的内容三个版本写法完全一致你用哪个版本写出来的代码都一样后面凡是三个版本写法有差异的地方我们都会单独指出来。二、数组的定义方式方式一声明 初始化直接赋值int[]scores{85,92,78,65,99};// 最常用适合值一开始就知道的场景比如写死的一组测试数据。方式二声明 指定长度int[]arrnewint[5];// 长度为 5元素默认值为 0arr[0]10;arr[1]20;适合长度由运行情况决定的场景。new 出来的数组元素有默认值数值类型为 0char 为 ‘\u0000’boolean 为 false引用类型为 nullint[]intsnewint[2];System.out.println(ints[0]);// 0数值类型默认 0String[]namesnewString[2];// String 是引用类型System.out.println(names[0]);// null方式三先声明后赋值int[]arr;arrnewint[]{1,2,3};// 注意不能省略 new int[] 写法中的类型两种写法int[] arr和int arr[]都合法但推荐int[] arr语义更清晰arr 是一个 int 数组。三、访问与修改数组元素通过下标访问下标范围0 ~ length-1int[]scores{85,92,78};System.out.println(scores[0]);// 85第一个元素System.out.println(scores[2]);// 78最后一个元素scores[1]100;// 修改第二个元素System.out.println(scores[1]);// 100越界会抛异常访问scores[3]会报ArrayIndexOutOfBoundsException这是最常见的数组错误先用length确认边界再访问。下标不仅能读查找某个值第一次出现的位置也要靠它int[]scores{85,92,78,92};inttarget92;for(inti0;iscores.length;i){if(scores[i]target){System.out.println(找到下标i);// 找到下标1break;// 只找第一个}}四、数组长度int[]scores{85,92,78,65,99};System.out.println(scores.length);// 5注意没有括号System.out.println(scores.length-1);// 4最后一个元素的下标length是数组的属性不是方法所以不加()最后一个元素的下标永远是length - 1。长度为 0 的数组也合法遍历时循环体一次都不执行int[]emptynewint[0];// 长度为 0 的数组System.out.println(empty.length);// 0五、遍历数组1. 普通 for 循环需要下标时int[]scores{85,92,78};for(inti0;iscores.length;i){System.out.println(第 i 个成绩scores[i]);}2. 增强 forforeach只读遍历for(intscore:scores){System.out.println(score);}语法for (元素类型 变量名 : 数组)。增强 for 无法知道当前下标也不能修改数组元素适合只读遍历。怎么选只读、不关心下标 → foreach需要下标、或要修改元素 → 普通 for。六、数组的常用操作1. 求最大值 / 最小值int[]nums{34,78,12,90,45};intmaxnums[0];// 假设第一个最大for(inti1;inums.length;i){if(nums[i]max){maxnums[i];}}System.out.println(最大值max);// 90思路是打擂台先假设第一个最大逐个比遇到更大的就换人。求最小值把换成即可。2. 求和与平均值intsum0;for(intn:nums){sumn;}System.out.println(平均值(double)sum/nums.length);注意先转成double再除否则整数相除会把小数丢掉259 / 5 得到 51 而不是 51.8。3. 数组排序Arrays 工具类importjava.util.Arrays;int[]nums{34,78,12,90,45};Arrays.sort(nums);// 升序排序System.out.println(Arrays.toString(nums));// [12, 34, 45, 78, 90]Arrays.toString()可以把数组打印成易读形式调试神器。排完序后最大值就是最后一个元素nums[nums.length - 1]。4. 数组拷贝int[]src{1,2,3,4,5};int[]destArrays.copyOf(src,src.length);// 复制全部int[]partArrays.copyOfRange(src,1,3);// 复制下标 1~2[2, 3]七、二维数组数组的元素还是数组就是二维数组int[][]matrix{{1,2,3},{4,5,6},{7,8,9}};System.out.println(matrix[0][1]);// 2第 0 行第 1 列System.out.println(matrix.length);// 3行数System.out.println(matrix[0].length);// 3第 0 行的列数内存结构要理解对matrix本身是长度为 3 的数组每个元素又是一个int[]。所以matrix.length是行数matrix[i].length是第 i 行的列数。遍历二维数组用嵌套循环for(inti0;imatrix.length;i){for(intj0;jmatrix[i].length;j){System.out.print(matrix[i][j] );}System.out.println();}注意二维数组的每一行长度可以不同不规则数组int[][]jaggednewint[3][];jagged[0]newint[]{1,2};jagged[1]newint[]{3,4,5};jagged[2]newint[]{6};System.out.println(jagged[0].length);// 2System.out.println(jagged[1].length);// 3// 遍历必须用 jagged[i].length不能假设每行一样长八、数组作为方法参数与返回值publicclassArrayDemo{publicstaticvoidmain(String[]args){int[]nums{3,1,4,1,5};printArray(nums);int[]reversedreverse(nums);printArray(reversed);}staticvoidprintArray(int[]arr){System.out.println(Arrays.toString(arr));}staticint[]reverse(int[]arr){int[]resultnewint[arr.length];for(inti0;iarr.length;i){result[i]arr[arr.length-1-i];}returnresult;}}注意方法内修改数组元素会直接影响原数组——因为数组是引用类型传的是地址。这里要区分改内容和换指向通过地址修改元素原数组跟着变让形参重新指向一个新数组原数组毫发无损staticvoidchangeElement(int[]arr){arr[0]999;// 改内容通过地址找到原数组}staticvoidchangeRef(int[]arr){arrnewint[]{7,7,7};// 换指向形参指向新数组与外部无关}int[]a{1,2,3};changeElement(a);System.out.println(a[0]);// 999原数组被改了int[]b{1,2,3};changeRef(b);System.out.println(b[0]);// 1原数组没变九、扩展知识1. Arrays 工具类全家桶java.util.Arrays是数组的瑞士军刀除了 sort 和 toString还有几个高频方法importjava.util.Arrays;int[]nums{34,78,12,90,45};Arrays.sort(nums);// 全部升序排序System.out.println(Arrays.toString(nums));// [12, 34, 45, 78, 90]// binarySearch 二分查找必须先排序找到返回下标找不到返回负数System.out.println(Arrays.binarySearch(nums,45));// 2System.out.println(Arrays.binarySearch(nums,100));// -6插入点取负再减 1// copyOf 可以按需截取int[]first3Arrays.copyOf(nums,3);System.out.println(Arrays.toString(first3));// [12, 34, 45]二维数组直接toString打印出来是一串地址要看内容必须用deepToStringint[][]m{{1,2},{3,4}};System.out.println(Arrays.toString(m));// [[Ixxx, [Ixxx] 不可读System.out.println(Arrays.deepToString(m));// [[1, 2], [3, 4]]2. 二维数组的内存布局栈matrix引用 ↓ 堆[ 行0引用 | 行1引用 | 行2引用 ] ← 外层数组长度 3 ↓ ↓ ↓ [1,2,3] [4,5,6] [7,8,9] ← 三个独立的一维数组对象每一行都是独立创建的一维数组对象所以行与行可以长度不同不规则数组matrix[i]拿到的只是某一行的引用。十、易错点1. 数组越界ArrayIndexOutOfBoundsExceptionint[]arr{10,20,30};// 下标只能是 0、1、2// ❌System.out.println(arr[3]);// 运行时报 ArrayIndexOutOfBoundsException// ✅ 用 length 兜底取最后一个元素System.out.println(arr[arr.length-1]);// 302. length 是属性不是方法// ❌ length 后面加括号编译错误intlenarr.length();// ✅ length 是数组的属性不加括号intlenarr.length;3. 引用赋值是别名不是拷贝int[]a{1,2,3};// ❌ b 只是 a 的别名两个变量指向同一个数组int[]ba;b[0]100;System.out.println(a[0]);// 100a 也被改了// ✅ 要独立副本用 Arrays.copyOfint[]cArrays.copyOf(a,a.length);c[0]999;System.out.println(a[0]);// 100互不影响4. foreach 里修改元素无效int[]arr{1,2,3};// ❌ 增强 for 取出的 x 是副本改它没用for(intx:arr){x0;}System.out.println(Arrays.toString(arr));// 还是 [1, 2, 3]// ✅ 要改元素必须用下标for(inti0;iarr.length;i){arr[i]0;}System.out.println(Arrays.toString(arr));// [0, 0, 0]5. new 出来的数组有默认值不是空的// ✅ 数值数组默认全是 0直接读不会报错int[]arrnewint[5];System.out.println(arr[0]);// 0// ❌ 把默认 0当成随机值或没赋值来理解容易写出隐蔽 bug// ✅ 引用类型数组默认是 null在 null 上调用方法会空指针String[]namesnewString[2];System.out.println(names[0]);// null// names[0].length(); // 运行时报 NullPointerException十一、小结数组长度固定、下标从 0 开始、越界抛异常下标本质是偏移量定义方式三种直接赋值、new int[长度]有默认值、先声明后赋值遍历用 for 或 foreach只读用 foreach需要下标或修改元素用普通 forArrays工具类提供 sort、binarySearch、toString、copyOf 等常用方法二维数组本质是数组的数组每一行是独立的一维数组可以长度不同数组是引用类型方法内改元素影响原数组重新指向不影响引用赋值是别名不是拷贝下一节学习方法定义、重载与递归。下一篇第8章 方法定义、重载与递归