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<String> 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<String> 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中的断点处就生效了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容