⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_22136439 「小夏陌」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

简介

Debug功能是我们平时学习或工作中,最常用的IDEA功能之一。可用来追踪代码流程,确认运行过程中参数变化,分析定位程序的错误,线上问题追踪,也可以用以学习第三方框架等。程序从一个黑盒,开启Debug功能后,就变的一丝不挂了,我们能明白每一步发生了什么,以及为什么发生。

看到这里你也许会说:“Debug有什么难的,点开Debug,一直下一步,梭哈到底不就完了吗,费那事干嘛?”那如果,你想回退怎么办,或你想更改参数值怎么办,重新发一次请求么?再或者需要远程调试时,你咋办,这些都是我们接下来要全面学习的内容。如果能熟练运用Debug功能的话,一定可以事半功倍。

注:开发工程中,用Debug代替Run,是个不错的习惯,可以让我们随时调试代码

接下来我会从这几个方面全面向大家介绍一下IDEA的Debug功能。

  • 准备工作
  • 主界面介绍
  • 各功能键详解
  • 查看变量
  • 更改变量值
  • 设置断点条件
  • 计算表达式
  • 回退操作
  • 强制返回
  • 多线程调试
  • Stream 调试
  • 远程Debug
  • 最后

准备工作

开始讲解Debug功能之前,我们需要准备以下工具:

  • IDEA
  • 调试代码

调试代码如下,我们之后的操作,大部分在此代码上进行。此为Leetcode上的经典题目:两数之和 的解法。

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
* Created By L
* Description:
*/
public class ToSum {

public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int sub = target - nums[i];
// 查找余数是否在表中,有则返回两个数的索引
// 没有则将其信息添加进哈希表
if (map.containsKey(sub)) {
return new int[]{map.get(sub), i};
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}

public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
int target = 9;
int[] ints = twoSum(nums, target);
System.out.println(Arrays.toString(ints));
}
}

Debug窗口

有同学的Debug窗口没有在IDEA底部显示,可通过双击Shift键方式打开搜索功能框,然后输入Debug,选择如图第二个选项,Debug窗口即会出现在底部。

当然,右键Debug Toolbar 我们还可以选择,让窗口出现在上下左右任意位置。

注:这里用的mac,快捷键和Windows不太一样,不过不影响大家学习技能,我用到的快捷键一定是两平台通用的,然后这里正好强迫下大家去实际操作一下,学过没用过,等于没学过。

主界面介绍

我们先来看下IDEA中Debug模式下的主界面。

  • Debug: 启动Debug功能。
  • 断点: 可在左边栏单击,以打上断点,以Debug方式运行到此时,程序会停下。
  • 服务按钮: 可以在这里关闭服务、启动服务、修改Debug配置,设置断点等。
  • 调试按钮: 共8个其中包括:Show Execution Point、Step Over (F8)、Step Into (F7)、Force Step Into、Step Out、Drop Frame、Run to Cursor、Evaluate Expression,后面会详细解释功能。
  • Frames: 显示了该线程调试所经过的所有方法,勾选右上角的漏斗(Show All Frames)按钮,就不会显示其它类库的方法了,否则这里会有一大堆的方法。
  • Variables: 可以查看变量具体的值,当然也可以修改。
  • Watches: 查看变量,可以将Variables区中的变量拖到Watches中查看,默认的Debug窗口,可能并没有这个窗口,你需要点击Variables窗口的“眼镜”👓图标,此窗口才会出现。再点击Watches列里“眼镜”👓图标,即可收回。
  • Debug窗口: 可以设置Debug窗口是否固定,或显示在哪个边栏,只需右键即可选择。你甚至可以拖动该Debug窗口,使其以独立窗口形式存在。

各功能键详解

接下来,我们依次讲讲服务按钮、调试按钮、Watches操作栏的作用:

服务按钮

服务按钮如上图中1区所示,从上至下,共10个按钮,IDEA版本不同,可能有小部分不一致。

  • Rerun ‘xxx’: 重新运行程序,点击会以Debug方式重启服务。
  • Edit Run Configuration:“xxx” : 更改运行时配置,其中包括运行Java版本更,环境变量等更改,还可以用于进行远程调试,之后详解。
  • Resume Program (F9): 恢复程序,从一个断点运行至下一个断点,没有断点则运行完整个程序。
  • Pause Program: 暂停程序,当我们执行某步时间太长,或遇到死循环时,可使用其暂停程序,如想继续执行,再点击Resume Program (F9)即可。
  • Stop ‘xxx’ (Ctrl + F2): 终止当前Debug程序。
  • View Breakpoints: 查看所有断点,还可选择按包、文件或class类进行断点分组,还可对断点进行一些配置,包括条件配置,多线程配置等,后面会详解。
  • Mute Breakpoints: 沉默所有断点,选择这个后,所有断点变为灰色,断点失效。再次点击,断点变为红色,有效。如果只想使某一个断点失效,可以在断点上右键取消Enabled,会显示空心的红圈,表示当前断点不可用。
  • Get Thread Dump: 导出当前堆栈信息,可用作其它分析。
  • Settings: 一些显示设置,可以设置是否在代码界面显示Values、Return Values等,开启即可。
  • Pin Tab: 是否固定当前Debug标签页。

调试按钮

调试按钮如上图中2区所示,从左至右,共9个按钮,也是我们平时除服务按钮外用的最多的操作了。

  • Show Execution Point : 定位,点击此按钮可定位到当前代码执行的行。
  • Step Over (F8): 步过,一行一行地往下走,如果执行到子方法,子方法内又无断点的情况下,会跳过子方法继续执行。
  • Step Into (F7): 步入,一行一行地往下走,如果执行到子方法,无论子方法有无断点,都会进入到子方法,子方法执行完后返回,继续执行。
  • Force Step Into (Alt + Shift + F7): 强制步入,能进入任何方法,查看底层源码的时候可以用这个进入官方类库的方法。
  • Step Out (Shift + F8): 步出,从步入的方法内退出到方法调用处,此时方法已执行完毕,只是还没有完成赋值。
  • Drop Frame: 回退断点,相当于后悔药,后面会详解。
  • Run to Cursor: 使程序运行至光标处,而光标处不需要打断点,运行过程中,有断点会在断点处停止。
  • Evaluate Expression…: 计算表达式,后面章节详细说明。
  • Trace Current Stream Chain: 显示Stream处理的整体过程。Lambda调试神器,之后会详解。

Watches操作栏

Watches窗口是观察变量变化的窗口,其操作栏如上图中3区所示,共6个按钮。

  • new Watch…: 新增观察变量。
  • Remove Watch: 删除观察变量
  • Move Watch Up: 向上移动观察变量。
  • Move Watch Down: 向下移动观察变量。
  • Duplicate Watch: 复制观察变量。
  • Show watches in variables tab: Watches不会默认打开为新窗口,点击“眼镜”👓图标后,才会打开为新窗口,再次点击会回到Variables边栏。

查看变量

查看变量值的方式有四种,这里我们首先确定我们的服务按钮中的Setting中有选择在代码界面显示Values、Return Values等。如下图所示即可,没找到也没关系,默认都会显示的。

在代码行的后面,有灰色的变量值显示。

光标悬停到参数上,也可显示当前变量信息。如果是复杂的变量,还可以点开查看详细变量值

也可在Variables里查看变量值,甚至是操作变量值,这里也是变量操作最多的地方。比如,我们运行到一半,想更改某一个变量的值怎么办?重新发一次请求吗,还是重新运行一次?其实不用这么麻烦,直接更改值即可!选择 Set Value… 即可

按下回车,当前target变量的值就改变了,而后可以继续运行。

在Watches也可以观察变量值,我们可以通过从Variables里拖变量到Watches中观察,也可以通过点击 new Watch… 添加观察变量。

这里和Variables的区别是:Variables窗口只会显示当前代码行能看到的变量,Watches只要添加了该变量,则会一直存在Watches窗口。

更改变量值

其实上一节,已经讲了如何在Variables窗口里更改变量值,这里再讲一种如何更改变量值,如下图所示,这里还可以把变量加入Watches窗口中。

比如这里我们这把num[0]改成了22

设置断点条件

想像一种情况,我们要遍历一个大集合,我们只有当集合为null时,才停下来,那我们难道要一次一次的点过去吗,点到900次的时候,你一时手快,跳过了怎么办?其实我们可以设置断点停留的条件,当条件满足的时候才停下,否则继续运行。以下方代码为例:

我们想在值为null值时,停下断点,看看是哪里为null,然后去更改数据库,进行如下设置即可。

只有当 n == null 这个条件为空时,这个断点才会停止!

计算表达式

我们在Debug时,想知道运行时某个方法的调用值或表达式的结果值,但是这个方法又没写在代码中,我们总是会添加上该方法代码,然后重新运行一次对吧。

其实不用重新运行也可以计算其值,点击Evaluate Expression…按键即可。比如,我想知道此时map.keySet()的值会是多少,直接点击Evaluate即可。

当然我们也可以计算一些复杂的表达式

这里的Evaluate功能其实就是:用当前代码的上下文环境,写出代码表达式,最后计算出临时的结果值。可方便我们的各种假设性代码实现,而不用每次都去重启代码,也不会影响原代码的正常运行。

回退操作

回退操作即是Debug中的后悔药,IDEA中只能回退到上一个方法调用处,并不能一步一步回退或是回退到上一个断点处。还有一点值得注意的是,回退只是重新走一下流程,之前的某些参数/数据的状态已经改变了的,是无法回退到之前的状态的,如对象、集合、更新了数据库数据等等。

比如,main方法调用A方法,A方法里删除了数据库里的一行数据,我们也执行了,这时我们回退,会回退到main方法中调用A方法处,我们A方法中的局部变量的确是回退了,但是数据库里的值已经被删了,回退不了了,其它第三方对象、集合原理也类似,回退不了。回退的方式很简单,在Frames窗口中,点击Drop Frame按键即可。

也可一次回退几个方法,比如我这里是main调用a方法,a调用b方法,b调用c方法,我这里直接选中a方法Frame,右键点击Drop Frame,这样就直接回退到main方法了,右键的Drop Frame和操作栏上的效果是一致的。

强制返回

想象一个场景,当前bug已经找到了,你不想再继续执行了,再执行后面的代码会删除数据,你不想执行了,此时你们怎么办?直接停掉整个服务吗?能不能只停掉当前线程,而不停掉整个服务?答案是可以的,我们给他一个返回值。这里使用Force Return强制返回即可。

比如这里,twoSum方法要返回int[],我们直接new int[]{2, 7}返回即可,当然我们也可以返回点让前端开心点的数据,哄哄前端。

或者我们直接抛出异常也是可以的

多线程调试

Debug默认使用时,会阻塞所有线程,只留当前线程。平时如果只是学习可能不会觉得麻烦,但是开发中,某个接口,其他人在用,难道要别人等一下,等你Debug完再用吗,其实不用的。只需要选中如下所示,即可多线程调试,没有打断点的线程会直接执行,打了断点的线程,才会被阻塞,当然,同一方法入口,也可以进入多个线程,在Frames中进行线程的切换即可。

也可选择 View Breakpoints,然后再选择Thread开启多线程调度模式。

Stream 调试

有没有发现,用Debug调试Lambda表达式时很难受,尤其是哪种一行十几个方法哪种,一写就是一串,写时爽的一P,Debug时反向爽的一P,如果你也有这些问题,那么接下来这个神器要好好学习一下了。

不知你有没有发现,调试按钮里Trace Current Stream Chain 按钮常年都是黑色的,为什么呢,因为他是做Stream调试的,只有断点在Stream表达式时才能使用,使用方法即断点在Stream时,点击该按钮即可。

看出该按钮的妙处了吧,能直接把每一步的转换方式或者结果展示在我们面前,而且还有Split Mode展示方式,能一步一步展示转换结果。

远程Debug

当我们遇到某些问题,只能在生产环境下复现,开发环境不能复现的bug时,我们就需要使用远程Debug功能了。只需要我们本地代码和远程部署的代码一致即可。当然还要在远程启动jar包时,加入-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=xxx参数,其中xxx为监听端口,详细步骤如下:

点击 Edit Configuration…

依次选择“+”,然后选择 Remote JVM Debug

然后填写一下配置,Name是之后启动的名称,Host 是远程服务器的 ip,port: 用于远程socket 连接的端口,注意不能和项目端口一致,否则会启动失败,然后,idea 会为我们自动生成一条命令行参数:-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

接着在远程启动jar时,加上我们的参数

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar xxx.jar

在远程项目启动成功后, 在本地以Debug方式运行第一步配置的服务

然后再请求远程原服务,我们在本地的断点就起作用了,是不是很秀

最后

至此IDEA Debug基本所有的知识点都介绍完了,欢迎大家关注,之后会有更精彩的知识点分享给大家!

文章目录
  1. 1. 简介
  2. 2. 准备工作
  3. 3. 主界面介绍
  4. 4. 各功能键详解
  5. 5. 查看变量
  6. 6. 更改变量值
  7. 7. 设置断点条件
  8. 8. 计算表达式
  9. 9. 回退操作
  10. 10. 强制返回
  11. 11. 多线程调试
  12. 12. Stream 调试
  13. 13. 远程Debug
  14. 14. 最后