Debug功能是我们平时学习或工作中,最常用的IDEA功能之一。可用来追踪代码流程,确认运行过程中参数变化,分析定位程序的错误,线上问题追踪,也可以用以学习第三方框架等。程序从一个黑盒,开启Debug功能后,就变的一丝不挂了,我们能明白每一步发生了什么,以及为什么发生。看到这里你也许会说:“Debug有什么难的,点开Debug,一直下一步,梭哈到底不就完了吗,费那事干嘛?”那如果,你想回退怎么办,或你想更改参数值怎么办,重新发一次请求么?再或者需要远程调试时,你咋办,这些都是我们接下来要全面学习的内容。如果能熟练运用Debug功能的话,一定可以事半功倍。
注:开发工程中,用Debug代替Run,是个不错的习惯,可以让我们随时调试代码
接下来我会从这几个方面全面向大家介绍一下IDEA的Debug功能。
开始讲解Debug功能之前,我们需要准备以下工具:
调试代码如下,我们之后的操作,大部分在此代码上进行。此为Leetcode上的经典题目:两数之和 的解法。
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Created By L
* Date: 2022/8/6 21:00
* Description:
*/
public class ToSum {
public static int[] twoSum(int[] nums, int target) {
Map 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窗口即会出现在底部。注:这里用的mac,快捷键和Windows不太一样,不过不影响大家学习技能,我用到的快捷键一定是两平台通用的,然后这里正好强迫下大家去实际操作一下,学过没用过,等于没学过。
我们先来看下IDEA中Debug模式下的主界面。
接下来,我们依次讲讲服务按钮、调试按钮、Watches操作栏的作用:
服务按钮如上图中1区所示,从上至下,共10个按钮,IDEA版本不同,可能有小部分不一致。
Resume Program (F9)
即可。调试按钮如上图中2区所示,从左至右,共9个按钮,也是我们平时除服务按钮外用的最多的操作了。
Watches窗口是观察变量变化的窗口,其操作栏如上图中3区所示,共6个按钮。
查看变量值的方式有四种,这里我们首先确定我们的服务按钮中的Setting中有选择在代码界面显示Values、Return Values等。如下图所示即可,没找到也没关系,默认都会显示的。
其实上一节,已经讲了如何在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开启多线程调度模式。
有没有发现,用Debug调试Lambda表达式时很难受,尤其是哪种一行十几个方法哪种,一写就是一串,写时爽的一P,Debug时反向爽的一P,如果你也有这些问题,那么接下来这个神器要好好学习一下了。不知你有没有发现,调试按钮里Trace Current Stream Chain 按钮常年都是黑色的,为什么呢,因为他是做Stream调试的,只有断点在Stream表达式时才能使用,使用方法即断点在Stream时,点击该按钮即可。
看出该按钮的妙处了吧,能直接把每一步的转换方式或者结果展示在我们面前,而且还有Split Mode展示方式,能一步一步展示转换结果。
当我们遇到某些问题,只能在生产环境下复现,开发环境不能复现的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基本所有的知识点都介绍完了,欢迎大家关注,之后会有更精彩的知识点分享给大家!