Java 入门(IDEA 高效调试 与 数组)

Java 入门(IDEA 高效调试 与 数组)

目录

一、先搞懂:什么是程序调试?

二、IDEA 调试

1. 调试三步基础

2. 核心调试操作

3. 进阶:条件断点

4. 调试面板看什么?

三、Java 数组

1. 为什么要用数组?

2. 数组创建与初始化(两种方式)

动态初始化(指定长度)

静态初始化(指定内容)

3. 数组遍历:3 种写法

4. 关键:数组是引用类型

5. 数组作为方法参数 / 返回值

6. 数组常用工具方法(java.util.Arrays)

7. 经典算法手写(面试常考)

8. 二维数组

四、调试 + 数组:实战组合技巧

五、总结


作为 Java 开发者,调试能力决定排错效率,数组功底决定代码质量。这篇文章把 IDEA 调试全套技巧、数组从基础到进阶的知识点一次性讲透,适合新手夯实基础、老手快速回顾。


一、先搞懂:什么是程序调试?

调试(Debugging)不是简单 “改代码”,而是定位问题 → 缩小范围 → 修复验证的完整过程。

  • 小代码:肉眼读 + 打印日志
  • 复杂项目:必须用 IDE 调试器

IDEA 内置的调试器是 Java 开发效率神器,掌握它能让你快速揪出隐藏 Bug。


二、IDEA 调试

1. 调试三步基础

  1. 打断点:代码行号左侧单击,出现红色圆点
  2. 启动调试:右键 → Debug
  3. 控制执行:用快捷键一步步观察变量与流程

2. 核心调试操作

操作快捷键作用
逐过程(不进方法)F8一行一行走,跳过方法内部
逐语句(进入方法)F7进入自定义方法内部
强制进入Alt+Shift+F7强行进入 JDK 库方法 / 无源码方法
跳出方法Shift+F8执行完当前方法并返回调用处
运行到光标Alt+F9直接跳到光标所在行
恢复程序F9跳到下一个断点 / 结束
重新调试Ctrl+F5重启调试会话
停止调试Ctrl+F2终止调试
查看所有断点Ctrl+Shift+F8统一管理断点

3. 进阶:条件断点

循环几百次只想在特定值停下?用条件断点

  1. 右键断点
  2. 填入条件(如 i == 20
  3. 调试只会在条件成立时暂停

4. 调试面板看什么?

  • Debugger 面板:调用栈 + 实时变量值 + 表达式计算
  • Console 面板:程序正常输出 / 异常信息
  • Mute Breakpoints:一键临时屏蔽所有断点

三、Java 数组

数组是 Java 最基础的数据结构,相同类型、连续内存、下标从 0 开始,这三个特征刻在心里。

1. 为什么要用数组?

不用数组:存 100 个成绩要定义 100 个变量。用数组:一行搞定,遍历、修改、传参都更简洁。

2. 数组创建与初始化(两种方式)

动态初始化(指定长度)
int[] arr = new int[5]; // 默认值:0 String[] strs = new String[3]; // 默认值:null 
静态初始化(指定内容)
// 完整写法 int[] arr1 = new int[]{1,2,3,4,5}; // 简写(推荐) int[] arr2 = {1,2,3,4,5}; 

⚠️ 注意:

  • 简写形式不能拆分两行写
  • 下标范围:[0, length),越界抛 ArrayIndexOutOfBoundsException

3. 数组遍历:3 种写法

  1. 普通 for 循环(可修改元素)
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } 
  1. for-each 循环(只读更简洁)
for (int num : arr) { System.out.println(num); } 
  1. Arrays.toString(快速打印)
System.out.println(Arrays.toString(arr)); 

4. 关键:数组是引用类型

  • 基本类型:变量存
  • 引用类型:变量存堆内存地址
  • arr1 = arr2指向同一块内存,不是拷贝
int[] a = {1,2,3}; int[] b = a; b[0] = 100; // a[0] 也会变成 100 
  • null:空引用,访问会抛 NullPointerException

5. 数组作为方法参数 / 返回值

  • 传基本类型:值传递,不影响原变量
  • 传数组:引用传递,方法内修改会影响原数组
  • 数组可直接作为返回值,返回一组数据

6. 数组常用工具方法(java.util.Arrays)

  • Arrays.toString(arr):数组转字符串
  • Arrays.copyOf(arr, len):数组拷贝(新对象)
  • Arrays.copyOfRange(arr, from, to):范围拷贝
  • Arrays.sort(arr):高效排序
  • Arrays.binarySearch(arr, key):有序数组二分查找
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(结果错误,不可信) */
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] */
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] */
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] */
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]] */

7. 经典算法手写(面试常考)

  • 顺序查找
  • 二分查找(有序数组)
  • 数组逆序
  • 自定义数组拷贝
/** * 自定义数组拷贝 * 核心:创建新数组,遍历原数组,逐个复制元素到新数组 * 对比 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; } }
/** * 数组逆序 * 核心:双指针法,首尾元素交换,直到指针相遇 * 优点:时间复杂度 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--; } } }

冒泡排序

/** * 冒泡排序 * 核心:相邻元素两两比较,大的元素逐步“冒泡”到数组末尾 * 优化:如果某一轮没有交换,说明数组已有序,直接退出(减少循环次数) * 时间复杂度:最坏 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; } } } }
/** * 二分查找(折半查找) * 核心:利用有序数组特性,每次排除一半元素,效率高(时间复杂度 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; } }
/** * 顺序查找(线性查找) * 核心:逐个遍历、逐一比较,找到返回下标,未找到返回-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; } }

8. 二维数组

本质:数组的数组

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(); } 

四、调试 + 数组:实战组合技巧

  1. 遍历数组时用条件断点,只看目标元素
  2. 调试面板直接查看数组每一位的值
  3. 下标越界时,用调试看循环边界是否正确
  4. 引用传递问题:在面板看地址是否相同

五、总结

  • 调试是程序员的基本功,IDEA 快捷键用熟效率翻倍
  • 数组是数据结构基石,连续内存、引用类型、下标 0 起步
  • 先会调试排错,再扎实掌握数组,Java 基础才算稳

把这篇收藏好,写代码、查 Bug、面试复习都能用得上。

Read more

【Electron教程】第17节 与原生模块交互(Node.js C++ Addons)

【Electron教程】第17节 与原生模块交互(Node.js C++ Addons)

🐋 第17节 与原生模块交互(Node.js C++ Addons):Electron开发进阶教程 ⌚老曹带你深入 Electron 的原生模块交互技术!本章节将全面解析如何调用原生模块以及使用 node-gyp编译模块。通过学习,你将掌握从基础到高级的知识点,并能设计出高效、可靠的原生模块集成方案。 📖 引言 在桌面应用开发中,有时需要调用底层系统功能或优化性能,这就需要用到 Node.js 的原生模块(C++ Addons)。Electron 支持通过 node-gyp 编译和加载这些模块,从而实现与底层系统的无缝交互。本章节将为你揭示如何优雅地集成原生模块,并确保其稳定性和性能。 🔑 本章内容概览 1. 🎯 调用原生模块 2. 🛠️ 使用 node-gyp 编译模块 3. 💡 最佳实践:如何优化原生模块的使用 4. ⚡ 重难点分析 5. 🏆 十大高频面试题及答案 1️⃣ 🎯 调用原生模块 📌 1.1

By Ne0inhk
【C++:智能指针】没有垃圾回收?智能指针来也!破解C++内存泄漏:智能指针原理、循环引用与线程安全详解

【C++:智能指针】没有垃圾回收?智能指针来也!破解C++内存泄漏:智能指针原理、循环引用与线程安全详解

🎬 个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》 《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬 艾莉丝的简介: 🎬 艾莉丝的C++专栏简介: 文章目录 * C++学习阶段的三个参考文档 * 1 ~> 前言:智能指针的使用场景 * 2 ~> RAII和智能指针的设计思路 * 2.1 理论:RAII * 2.2 最佳实践 * 2.3 实践RAII:核心思想 * 3 ~> C++标准库智能指针的使用 * 3.1 理论

By Ne0inhk
C++之《程序员自我修养》读书总结(5)

C++之《程序员自我修养》读书总结(5)

《程序员自我修养》读书总结(五) Author: Once Day Date: 2026年2月12日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文章可参考专栏: 书籍阅读_Once-Day的博客-ZEEKLOG博客 参考文章:《程序员的自我修养》读书笔记 | Zachary’s blog《程序员的自我修养》阅读笔记 - T0fV404 - 博客园读书笔记:《程序员的自我修养》 - 楷哥 - 博客园 文章目录 * 《程序员自我修养》读书总结(五) * 5. Windows PE/COFF 格式 * 5.1 发展历史 * 5.2 mingw-w64 工具链 * 5.

By Ne0inhk
【C++】C++异常

【C++】C++异常

🎬 个人主页:MSTcheng · ZEEKLOG 🌱 代码仓库 :MSTcheng · Gitee 🔥 精选专栏: 《C语言》 《数据结构》 《算法学习》 《C++由浅入深》 💬座右铭:路虽远行则将至,事虽难做则必成! 在前面的文章中,我们已经介绍了C++11的一些新特性。本文将和下一篇一起为大家讲解C++的最后两个重要主题:异常处理和智能指针。 文章目录 * 一、异常的概念及使用 * 1.1异常的概念 * 1.2异常的分类 * 1.3异常的抛出与捕获 * 1.4栈展开 * 1.5 查找匹配的处理代码 * 1.6异常重新抛出 * 1.7异常的安全问题 * 1.8异常规范 * 二、总结 一、异常的概念及使用 1.1异常的概念 异常(Exception)是指在程序执行过程中发生的意外或错误情况,

By Ne0inhk