IDEA调试技巧进阶

一、本文目的

日常开发工作中,大多数开发者只会简单地设置断点、启动调试、单步执行这三个操作,资深一点的可能还会表达式求值,设置条件断点等。

然而这些都不足以发挥IDEA调试功能的全部作用,手持利器而不知道怎么用,实在太可惜了。所以,本文来介绍下大家可能还不是很熟悉的调试技巧,助力大家更有效率地调试代码。

二、常用调试技巧

下图是开发过程中在IDEA的调试面板页最常使用的调试功能,下面我们一个个地说下它们代表的含义。

常用调试功能
  • 单步调试Sep Over
    从当前断点处一步一步地往下执行,遇到调用方法不会步入,倘若已经执行到本方法的最后一行,下一次Sep Over就会返回上层调用;

  • 步入Step Into

    当前断点调用了一个本地的方法时,可以使用该功能进入被调用的方法中;

  • 强制步入Force Step Into

    当前断点调用了一个类库方法时(引用的jar包),可以使用该功能进入被调用的方法中;

  • 步出Step Out

    返回调用当前方法的调用处;

  • Run To Cursor

    设置好光标位置后,点击此功能直接运行到光标所在行;

  • Resume Program

    按照程序既定逻辑,运行到下一个有效断点处;

三、后悔药

假设在如下的代码中,我们在eatFruit方法中不慎运行到了方法的最后一行,但是此时我们有些疑惑,一开始的if语句为什么判断为false,但是此时已经不能后退到那一行了,我们该停止当前调试重新来过吗?

    public static void main(String[] args) {
        List fruitList = new ArrayList<>();
        fruitList.add("apple");
        fruitList.add("orange");
        fruitList.add("banana");

        for(String fruit : fruitList){
            eatFruit(fruit);
        }
    }

    private static Boolean eatFruit(String fruit){
        if(StringUtils.isEmpty(fruit)){
            return Boolean.FALSE;
        }
        log.info("I eat {}",fruit);
        return Boolean.TRUE;
    }

我们可以使用Drop Frame功能,回退对当前方法的调用,回到调用处,再次点击Step Into又能重新进到eatFruit方法中了,俗称后悔药。

Drop Frame实现后悔药——后悔之前
Drop Frame实现后悔药——后悔之后

我们注意到,Drop Frame之前和之后的fruit对象的值都是apple,说明确实是有效的。

四、条件断点

在如上的示例中,我们的for循环将迭代三次,如果我们只想验证当fruit值为banana的情况该怎么操作呢?

在设置断点处,右击断点,会弹出断点设置界面,在其中的Condition中输入你想要满足的断点条件,选择右下角Done即可。

条件断点

再次启动调试,会发现,前两次迭代都没有在该断点处暂停,只有第三次迭代,当fruit值为banana的时候,断点才有效。

五、热修改

当我们运行到断点处时,如果当前变量fruit的值不是我们想要的,我们想看看当值为pear的时候,程序会发生什么,那么可以在变量区选中该变量,然后右击选择Set Value,然后就可以将变量修改为你想要的值,再进行程序的调试。

六、表达式求值

当我们运行到断点处时,我们可以使用表达式来求解程序中没有的逻辑,比如我想查看当前变量fruit的长度,那么在弹出来的表达式求值中输入fruit.length(),回车执行即可。

表达式求值

除此之外,我们还可以使用表达式求值来修改变量的值,增加集合中的元素等操作。

七、新建变量监控

在变量区,变量列表的内容是跟随程序的运行而随时改变的,如果我们想一直查看某个变量下的某个属性值,或者使用表达式求值某个值,那么每次都需要手动操作,很是麻烦。

新建变量监控,对属性右键选择Add to Watches就可以让我们关心的值一直展示在watch列表中,随着程序的执行,自动求值,非常方便。

八、查看内存对象

我们想查看当前所有的内存对象可以打开Memeory选项卡,在右下侧过滤所有的对象类型,双击后在弹出框中会展示所有该类型的对象,然后可以通过condition对所有该类型的对象进行筛选。

查看内存对象

九、多线程调试

我们需要先改一下实验用例:

@Slf4j
public class ListTest {

    public static Integer count = 0;

    public static void main(String[] args) {
        List fruitList = new ArrayList<>();
        fruitList.add("apple");
        fruitList.add("orange");
        fruitList.add("banana");

        for(String fruit : fruitList){
            FruitThread fruitThread = new FruitThread();
            fruitThread.setFruitName(fruit);
            Thread thread = new Thread(fruitThread);
            thread.start();
        }
    }
}
@Slf4j
@Data
public class FruitThread implements Runnable {
    private String fruitName;

    @Override
    public void run(){
        if(ListTest.count < 1){
            ListTest.count++;
        }
        log.info("I eat {}, current count is {}",fruitName, ListTest.count);
    }
}

在这个例子中,我们将会启动三个线程,这三个线程都会对全局共享变量count进行修改,这是一件麻烦的事情,我们想看看是否是线程安全的,当然这里其实是线程不安全的哈,但是因为for循环次数太少了,每次执行的结果看上去都是线程安全的,那么我们就可以使用多线程调试来模拟线程不安全的场景。

我们可以在子线程逻辑的ListTest.count++;处打上断点,然后,右击断点,选择Thread模式;

然后启动调试,此时在调试面板中会出现三个线程运行到此处都暂停了,它们都进入了if判断中。

多线程调试

然后再全部放行。

可以看到,日志输出的结果出现了count大于1的值,说明这里确实是线程不安全的。

十、主动抛出异常

在断点调试的时候,我们想看看如果异常发生了,程序是否有问题。但是此处的异常很难模拟,比如调用三方库函数,我们不想改动代码就能模拟出调用返回了一个异常。

可以在调试界面的左侧选择当前的进程,然后右击,选择Throw Exception,在弹出框内给出具体的想要抛出的异常类型。

主动抛出异常
创建需要抛出的异常

十一、远程调试

远程调试的前提条件是,本地IDEA中调试的代码和服务器上正在运行的代码要严格一致。

我们准备如下的一个示例:

@Slf4j
@RestController
public class UserMailRest {

    @GetMapping("/getUserMail")
    public String getUserMail() {
        log.info("success");
        return "success";
    }
}

本地IDEA实验成功后,使用maven clean package打包成jar包,然后找到这个jar包。

使用如下命令启动该jar包:

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

或者

java -jar -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y,address=5005 demo.jar

两个命令都可以,唯一需要注意的是,address可以使用自己认为合适的。

此时,正常的话,命令行显示应用已经启动成功,正在监听8080端口,等待请求的到来,同时也在监听5005端口,等待远程调试的开始。

现在,我们在IDEA工具栏中的Run中找到Edit Configurations,然后在弹出的设置界面,选择左上角的加号,新增一个Remote类型的配置。

IDEA设置远程调试

Name可以自己取,host填写正在运行demo.jar的服务器地址,端口号保持和服务器上运行jar时设置的address参数中的端口号保持一致,最后,选择想要使用的代码模块。

点击右下角Apply之后,就可以在IDEA右上角选择该配置,然后启动调试了。

IDEA启动远程调试

正常的话,控制台应该提示如下信息:

Connected to the target VM, address: 'localhost:5005', transport: 'socket'

然后在IDEA的代码逻辑中设置你需要的断点,比如在log.info处,然后发起一个请求:

http://localhost:8080/getUserMail

此时,IDEA中的断点处就生效了。

你可能感兴趣的:(IDEA调试技巧进阶)