目录
[二、IDEA 调试](#二、IDEA 调试)
[1. 调试三步基础](#1. 调试三步基础)
[2. 核心调试操作](#2. 核心调试操作)
[3. 进阶:条件断点](#3. 进阶:条件断点)
[4. 调试面板看什么?](#4. 调试面板看什么?)
[三、Java 数组](#三、Java 数组)
[1. 为什么要用数组?](#1. 为什么要用数组?)
[2. 数组创建与初始化(两种方式)](#2. 数组创建与初始化(两种方式))
[3. 数组遍历:3 种写法](#3. 数组遍历:3 种写法)
[4. 关键:数组是引用类型](#4. 关键:数组是引用类型)
[5. 数组作为方法参数 / 返回值](#5. 数组作为方法参数 / 返回值)
[6. 数组常用工具方法(java.util.Arrays)](#6. 数组常用工具方法(java.util.Arrays))
[7. 经典算法手写(面试常考)](#7. 经典算法手写(面试常考))
[8. 二维数组](#8. 二维数组)
[四、调试 + 数组:实战组合技巧](#四、调试 + 数组:实战组合技巧)
作为 Java 开发者,调试能力决定排错效率,数组功底决定代码质量。这篇文章把 IDEA 调试全套技巧、数组从基础到进阶的知识点一次性讲透,适合新手夯实基础、老手快速回顾。
一、先搞懂:什么是程序调试?
调试(Debugging)不是简单 "改代码",而是定位问题 → 缩小范围 → 修复验证的完整过程。
- 小代码:肉眼读 + 打印日志
- 复杂项目:必须用 IDE 调试器
IDEA 内置的调试器是 Java 开发效率神器,掌握它能让你快速揪出隐藏 Bug。
二、IDEA 调试
1. 调试三步基础
- 打断点:代码行号左侧单击,出现红色圆点
- 启动调试:右键 → Debug
- 控制执行:用快捷键一步步观察变量与流程
2. 核心调试操作
| 操作 | 快捷键 | 作用 |
|---|---|---|
| 逐过程(不进方法) | F8 | 一行一行走,跳过方法内部 |
| 逐语句(进入方法) | F7 | 进入自定义方法内部 |
| 强制进入 | Alt+Shift+F7 | 强行进入 JDK 库方法 / 无源码方法 |
| 跳出方法 | Shift+F8 | 执行完当前方法并返回调用处 |
| 运行到光标 | Alt+F9 | 直接跳到光标所在行 |
| 恢复程序 | F9 | 跳到下一个断点 / 结束 |
| 重新调试 | Ctrl+F5 | 重启调试会话 |
| 停止调试 | Ctrl+F2 | 终止调试 |
| 查看所有断点 | Ctrl+Shift+F8 | 统一管理断点 |
3. 进阶:条件断点
循环几百次只想在特定值停下?用条件断点:
- 右键断点
- 填入条件(如
i == 20) - 调试只会在条件成立时暂停
4. 调试面板看什么?
- Debugger 面板:调用栈 + 实时变量值 + 表达式计算
- Console 面板:程序正常输出 / 异常信息
- Mute Breakpoints:一键临时屏蔽所有断点
三、Java 数组
数组是 Java 最基础的数据结构,相同类型、连续内存、下标从 0 开始,这三个特征刻在心里。
1. 为什么要用数组?
不用数组:存 100 个成绩要定义 100 个变量。用数组:一行搞定,遍历、修改、传参都更简洁。
2. 数组创建与初始化(两种方式)
动态初始化(指定长度)
java
int[] arr = new int[5]; // 默认值:0
String[] strs = new String[3]; // 默认值:null
静态初始化(指定内容)
java
// 完整写法
int[] arr1 = new int[]{1,2,3,4,5};
// 简写(推荐)
int[] arr2 = {1,2,3,4,5};
⚠️ 注意:
- 简写形式不能拆分两行写
- 下标范围:
[0, length),越界抛ArrayIndexOutOfBoundsException
3. 数组遍历:3 种写法
- 普通 for 循环(可修改元素)
java
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
- for-each 循环(只读更简洁)
java
for (int num : arr) {
System.out.println(num);
}
- Arrays.toString(快速打印)
javascript
System.out.println(Arrays.toString(arr));
4. 关键:数组是引用类型
- 基本类型:变量存值
- 引用类型:变量存堆内存地址
arr1 = arr2是指向同一块内存,不是拷贝
java
int[] a = {1,2,3};
int[] b = a;
b[0] = 100; // a[0] 也会变成 100
null:空引用,访问会抛NullPointerException
5. 数组作为方法参数 / 返回值
- 传基本类型:值传递,不影响原变量
- 传数组:引用传递,方法内修改会影响原数组
- 数组可直接作为返回值,返回一组数据
6. 数组常用工具方法(java.util.Arrays)
Arrays.toString(arr):数组转字符串
java
import java.util.Arrays;
/**
* Arrays.toString():数组转字符串
* 特点:
* 1. 一维数组直接输出易读格式
* 2. 多维数组需用 Arrays.deepToString()
* 3. 空数组输出 []
*/
public class ArraysToStringDemo {
public static void main(String[] args) {
// 测试1:普通int数组
int[] intArr = {1, 2, 3, 4, 5};
System.out.println("int数组转字符串:" + Arrays.toString(intArr));
// 测试2:字符串数组
String[] strArr = {"Java", "数组", "工具类"};
System.out.println("字符串数组转字符串:" + Arrays.toString(strArr));
// 测试3:空数组
int[] emptyArr = new int[0];
System.out.println("空数组转字符串:" + Arrays.toString(emptyArr));
// 测试4:多维数组(注意:一维toString不适用,需用deepToString)
int[][] multiArr = {{1,2}, {3,4}};
System.out.println("多维数组用toString:" + Arrays.toString(multiArr)); // 输出地址
System.out.println("多维数组用deepToString:" + Arrays.deepToString(multiArr)); // 正确输出
}
}
/*结果:
int数组转字符串:[1, 2, 3, 4, 5]
字符串数组转字符串:[Java, 数组, 工具类]
空数组转字符串:[]
多维数组用toString:[[I@7852e922, [I@4e25154f]
多维数组用deepToString:[[1, 2], [3, 4]]
*/
Arrays.copyOf(arr, len):数组拷贝(新对象)
java
import java.util.Arrays;
/**
* Arrays.copyOf():数组拷贝
* 特点:
* 1. 返回新数组,原数组不受影响(深拷贝)
* 2. len < 原长度:截取前len个元素
* 3. len > 原长度:补默认值(int补0,String补null)
*/
public class ArraysCopyOfDemo {
public static void main(String[] args) {
int[] originalArr = {10, 20, 30, 40, 50};
System.out.println("原数组:" + Arrays.toString(originalArr));
// 场景1:拷贝长度 = 原长度(完整拷贝)
int[] copyFull = Arrays.copyOf(originalArr, originalArr.length);
System.out.println("完整拷贝:" + Arrays.toString(copyFull));
// 场景2:拷贝长度 < 原长度(截取)
int[] copyShort = Arrays.copyOf(originalArr, 3);
System.out.println("截取前3个元素:" + Arrays.toString(copyShort));
// 场景3:拷贝长度 > 原长度(补默认值)
int[] copyLong = Arrays.copyOf(originalArr, 7);
System.out.println("补0到7个元素:" + Arrays.toString(copyLong));
// 验证:修改新数组,原数组不变
copyFull[0] = 999;
System.out.println("修改后新数组:" + Arrays.toString(copyFull));
System.out.println("原数组仍不变:" + Arrays.toString(originalArr));
}
}
/*结果:
原数组:[10, 20, 30, 40, 50]
完整拷贝:[10, 20, 30, 40, 50]
截取前3个元素:[10, 20, 30]
补0到7个元素:[10, 20, 30, 40, 50, 0, 0]
修改后新数组:[999, 20, 30, 40, 50]
原数组仍不变:[10, 20, 30, 40, 50]
*/
Arrays.copyOfRange(arr, from, to):范围拷贝
java
import java.util.Arrays;
/**
* Arrays.copyOfRange():范围拷贝
* 特点:
* 1. 左闭右开:[from, to)
* 2. from = 0 且 to = 原长度 → 等价于 copyOf
* 3. 下标越界会抛 ArrayIndexOutOfBoundsException
*/
public class ArraysCopyOfRangeDemo {
public static void main(String[] args) {
String[] originalArr = {"A", "B", "C", "D", "E", "F"};
System.out.println("原数组:" + Arrays.toString(originalArr));
// 场景1:拷贝中间范围(下标1到4,包含1,不包含4)
String[] range1 = Arrays.copyOfRange(originalArr, 1, 4);
System.out.println("拷贝[1,4):" + Arrays.toString(range1)); // [B, C, D]
// 场景2:拷贝从0到末尾(完整拷贝)
String[] range2 = Arrays.copyOfRange(originalArr, 0, originalArr.length);
System.out.println("拷贝完整数组:" + Arrays.toString(range2));
// 场景3:拷贝到超出原长度(补默认值null)
String[] range3 = Arrays.copyOfRange(originalArr, 3, 8);
System.out.println("拷贝[3,8)(补null):" + Arrays.toString(range3));
}
}
/*结果:
原数组:[A, B, C, D, E, F]
拷贝[1,4):[B, C, D]
拷贝完整数组:[A, B, C, D, E, F]
拷贝[3,8)(补null):[D, E, F, null, null]
*/
Arrays.sort(arr):高效排序
java
import java.util.Arrays;
/**
* Arrays.sort():数组排序
* 特点:
* 1. 基本类型数组:快速排序/双轴快排,效率高
* 2. 直接修改原数组(原地排序)
* 3. 字符串数组:按Unicode编码升序(字母A-Z对应升序)
*/
public class ArraysSortDemo {
public static void main(String[] args) {
// 场景1:int数组升序排序
int[] intArr = {5, 2, 9, 1, 5, 6};
System.out.println("排序前int数组:" + Arrays.toString(intArr));
Arrays.sort(intArr);
System.out.println("排序后int数组:" + Arrays.toString(intArr));
// 场景2:字符串数组排序
String[] strArr = {"Java", "Python", "C", "JavaScript"};
System.out.println("\n排序前字符串数组:" + Arrays.toString(strArr));
Arrays.sort(strArr);
System.out.println("排序后字符串数组:" + Arrays.toString(strArr));
// 场景3:指定范围排序(sort(arr, from, to):左闭右开)
int[] rangeArr = {10, 5, 8, 2, 7, 1};
System.out.println("\n排序前范围数组:" + Arrays.toString(rangeArr));
Arrays.sort(rangeArr, 1, 5); // 只排序下标1到4的元素
System.out.println("排序[1,5)后:" + Arrays.toString(rangeArr));
}
}
/*结果:
排序前int数组:[5, 2, 9, 1, 5, 6]
排序后int数组:[1, 2, 5, 5, 6, 9]
排序前字符串数组:[Java, Python, C, JavaScript]
排序后字符串数组:[C, Java, JavaScript, Python]
排序前范围数组:[10, 5, 8, 2, 7, 1]
排序[1,5)后:[10, 2, 5, 7, 8, 1]
*/
Arrays.binarySearch(arr, key):有序数组二分查找
java
import java.util.Arrays;
/**
* Arrays.binarySearch():二分查找
* 前提:数组必须是升序排序后的!
* 特点:
* 1. 找到:返回元素下标
* 2. 未找到:返回 -(插入点) - 1
* 3. 有重复元素:返回任意一个匹配下标(不保证是第一个)
*/
public class ArraysBinarySearchDemo {
public static void main(String[] args) {
int[] sortedArr = {1, 2, 3, 5, 7, 9, 10}; // 必须先排序!
System.out.println("有序数组:" + Arrays.toString(sortedArr));
// 场景1:查找存在的元素
int key1 = 7;
int index1 = Arrays.binarySearch(sortedArr, key1);
System.out.println("查找" + key1 + ":下标=" + index1);
// 场景2:查找不存在的元素
int key2 = 4;
int index2 = Arrays.binarySearch(sortedArr, key2);
System.out.println("查找" + key2 + ":返回值=" + index2 + "(插入点是3,-(3)-1=-4)");
// 场景3:查找超出范围的元素
int key3 = 11;
int index3 = Arrays.binarySearch(sortedArr, key3);
System.out.println("查找" + key3 + ":返回值=" + index3 + "(插入点是7,-(7)-1=-8)");
// 错误示例:未排序数组查找(结果不可靠)
int[] unsortedArr = {5, 2, 9};
int index4 = Arrays.binarySearch(unsortedArr, 2);
System.out.println("\n未排序数组查找2:" + index4 + "(结果错误,不可信)");
}
}
/*结果:
有序数组:[1, 2, 3, 5, 7, 9, 10]
查找7:下标=4
查找4:返回值=-4(插入点是3,-(3)-1=-4)
查找11:返回值=-8(插入点是7,-(7)-1=-8)
未排序数组查找2:-1(结果错误,不可信)
*/
7. 经典算法手写(面试常考)
- 顺序查找
java
/**
* 顺序查找(线性查找)
* 核心:逐个遍历、逐一比较,找到返回下标,未找到返回-1
* 适用:无序/有序数组都能查,优点是简单,缺点是效率低(时间复杂度 O(n))
*/
public class SequentialSearch {
public static void main(String[] args) {
// 测试数组(无序也能查)
int[] arr = {8, 3, 9, 7, 2, 5, 1, 6, 4};
int target = 7;
// 调用查找方法
int index = sequentialSearch(arr, target);
// 输出结果
if (index != -1) {
System.out.println("找到目标值 " + target + ",下标为:" + index);
} else {
System.out.println("未找到目标值 " + target);
}
}
/**
* 顺序查找核心方法
* @param arr 待查找的数组
* @param target 要找的目标值
* @return 找到返回下标,未找到返回-1
*/
public static int sequentialSearch(int[] arr, int target) {
// 健壮性处理:数组为空/长度为0,直接返回-1
if (arr == null || arr.length == 0) {
return -1;
}
// 遍历数组,逐个比较
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return i; // 找到立即返回下标,无需继续遍历
}
}
// 遍历完都没找到,返回-1
return -1;
}
}
- 二分查找(有序数组)
java
/**
* 二分查找(折半查找)
* 核心:利用有序数组特性,每次排除一半元素,效率高(时间复杂度 O(log₂n))
* 前提:数组必须是有序的(升序/降序,代码需对应调整)
*/
public class BinarySearch {
public static void main(String[] args) {
// 测试数组(必须是有序数组,这里用升序)
int[] sortedArr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int target = 5;
// 调用二分查找方法
int index = binarySearch(sortedArr, target);
// 输出结果
if (index != -1) {
System.out.println("找到目标值 " + target + ",下标为:" + index);
} else {
System.out.println("未找到目标值 " + target);
}
}
/**
* 二分查找核心方法(升序数组)
* @param sortedArr 有序数组(升序)
* @param target 要找的目标值
* @return 找到返回下标,未找到返回-1
*/
public static int binarySearch(int[] sortedArr, int target) {
// 健壮性处理
if (sortedArr == null || sortedArr.length == 0) {
return -1;
}
// 定义左右指针
int left = 0;
int right = sortedArr.length - 1;
// 循环条件:左指针 <= 右指针(注意是 <=,否则会漏查最后一个元素)
while (left <= right) {
// 计算中间下标(避免溢出:等价于 (left + right) / 2,但更安全)
int mid = left + (right - left) / 2;
if (sortedArr[mid] == target) {
return mid; // 找到目标,返回下标
} else if (sortedArr[mid] < target) {
// 目标在右半部分,左指针右移
left = mid + 1;
} else {
// 目标在左半部分,右指针左移
right = mid - 1;
}
}
// 循环结束仍未找到,返回-1
return -1;
}
}
-
冒泡排序
java/** * 冒泡排序 * 核心:相邻元素两两比较,大的元素逐步"冒泡"到数组末尾 * 优化:如果某一轮没有交换,说明数组已有序,直接退出(减少循环次数) * 时间复杂度:最坏 O(n²),最好 O(n)(已排序数组) */ public class BubbleSort { public static void main(String[] args) { // 测试数组 int[] arr = {8, 3, 9, 7, 2, 5, 1, 6, 4}; System.out.println("排序前:" + Arrays.toString(arr)); // 调用冒泡排序方法 bubbleSort(arr); // 输出结果 System.out.println("排序后:" + Arrays.toString(arr)); } /** * 冒泡排序核心方法(升序) * @param arr 待排序的数组(直接修改原数组) */ public static void bubbleSort(int[] arr) { // 健壮性处理 if (arr == null || arr.length <= 1) { return; // 空数组/只有1个元素,无需排序 } int length = arr.length; // 外层循环:控制排序轮数(最多 length-1 轮) for (int i = 0; i < length - 1; i++) { // 标记:本轮是否发生交换 boolean swapped = false; // 内层循环:每轮比较到 length-1-i 位置(后i个元素已排好序) for (int j = 0; j < length - 1 - i; j++) { // 相邻元素比较,前 > 后则交换(升序) if (arr[j] > arr[j + 1]) { // 交换两个元素 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; // 标记发生了交换 swapped = true; } } // 优化:本轮无交换,说明数组已有序,直接退出循环 if (!swapped) { break; } } } } -
数组逆序
java
/**
* 数组逆序
* 核心:双指针法,首尾元素交换,直到指针相遇
* 优点:时间复杂度 O(n),空间复杂度 O(1)(原地逆序,不额外占用空间)
*/
public class ReverseArray {
public static void main(String[] args) {
// 测试数组
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println("逆序前:" + Arrays.toString(arr));
// 调用逆序方法
reverseArray(arr);
// 输出结果
System.out.println("逆序后:" + Arrays.toString(arr));
}
/**
* 数组逆序核心方法(原地逆序,直接修改原数组)
* @param arr 待逆序的数组
*/
public static void reverseArray(int[] arr) {
// 健壮性处理
if (arr == null || arr.length <= 1) {
return; // 空数组/只有1个元素,无需逆序
}
// 定义首尾指针
int left = 0;
int right = arr.length - 1;
// 循环交换:左指针 < 右指针时交换
while (left < right) {
// 交换首尾元素
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
// 左指针右移,右指针左移
left++;
right--;
}
}
}
- 自定义数组拷贝
java
/**
* 自定义数组拷贝
* 核心:创建新数组,遍历原数组,逐个复制元素到新数组
* 对比 JDK 自带的 Arrays.copyOf:逻辑一致,帮你理解底层原理
*/
public class CustomArrayCopy {
public static void main(String[] args) {
// 测试数组
int[] originalArr = {8, 3, 9, 7, 2, 5, 1, 6, 4};
System.out.println("原数组:" + Arrays.toString(originalArr));
// 调用自定义拷贝方法
int[] copyArr = customCopyOf(originalArr, originalArr.length);
System.out.println("拷贝数组:" + Arrays.toString(copyArr));
// 验证:修改拷贝数组,原数组不受影响(证明是深拷贝)
copyArr[0] = 100;
System.out.println("修改后拷贝数组:" + Arrays.toString(copyArr));
System.out.println("修改后原数组:" + Arrays.toString(originalArr));
}
/**
* 自定义数组拷贝方法
* @param original 原数组
* @param newLength 新数组长度(可大于/小于原数组长度)
* @return 新的拷贝数组
*/
public static int[] customCopyOf(int[] original, int newLength) {
// 健壮性处理:原数组为空
if (original == null) {
return null;
}
// 1. 创建新数组(指定长度)
int[] newArr = new int[newLength];
// 2. 确定要拷贝的元素个数(取原数组长度和新长度的较小值)
int copyLength = Math.min(original.length, newLength);
// 3. 遍历原数组,逐个复制到新数组
for (int i = 0; i < copyLength; i++) {
newArr[i] = original[i];
}
// 4. 返回新数组(如果新长度 > 原长度,多余位置默认值为0)
return newArr;
}
}
8. 二维数组
本质:数组的数组
java
int[][] arr = {{1,2},{3,4},{5,6}};
// 遍历
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();
}
四、调试 + 数组:实战组合技巧
- 遍历数组时用条件断点,只看目标元素
- 调试面板直接查看数组每一位的值
- 下标越界时,用调试看循环边界是否正确
- 引用传递问题:在面板看地址是否相同
五、总结
- 调试是程序员的基本功,IDEA 快捷键用熟效率翻倍
- 数组是数据结构基石,连续内存、引用类型、下标 0 起步
- 先会调试排错,再扎实掌握数组,Java 基础才算稳
把这篇收藏好,写代码、查 Bug、面试复习都能用得上。