大家好,我是一航!
写Bug、改Bug几乎占据了程序员日常工作的绝大部分时间,如果你能掌握一手调试代码的绝技,相信工作效率必定会得到大幅度的提升;
IDEA 就为我们提供了很多简单且非常强大的调试功能,但是发现有些小伙伴并没有完全用上,今天就一起来玩一下那些Debug的奇淫巧计;30来个功能及调试小技巧,学完后让你开发调试效率提升个10倍!争取做到早上9点上班,10点就能下班(小声说:是晚上10点【手动狗头】)
本文的目录:
IDEA的Debug控制台在整个窗口的左下方;
Step Into
不同的是,会进入JDK的方法;
(Force)Step Into
一起使用
行断点的图标是一个 圆形的红点,在需要断点的代码行头点击,即可添加断点
将断点打在某个具体的方法上,方法执行的时候,会进入断点;
举个方法调试最常用的Debug场景:
当阅读源码或者自己写业务需求的时候,经常会用到策略、模板方法等设计模式;在调试的时候,需要知道,当前接口方法或者抽象方法的执行,到底是走的哪一个具体的实现,用方法调试就能很方便的找到;如下示例;
接口Service
有两个具体的实现:ServiceA
和ServiceB
,分别实现了接口的method
方法,调试的过程中就可以将断点打在接口的method方法上;当我们在Main方法中实例化了ServiceB,断点就自动进入到ServiceB的method()方法了;
接口Service
public interface Service {
void method();
}
public class ServiceA implements Service{
@Override
public void method() {
System.out.println("Service A");
}
}
public class ServiceB implements Service{
@Override
public void method() {
System.out.println("Service B");
}
}
Main
public class Main {
public static void main(String[] args) {
Service serviceB = new ServiceB();
serviceB.method();
}
}
在属性的行头点击即可添加一个小眼睛一样的属性监听断点,用于监听某个属性的读写变化过程;
异常断点是开发、调试的时候经常用到的一个功能,用于快速定位到那行代码出现了异常;
设置方式:
Ctrl + Shift + F8
打开配置窗口;+
号;Java Exception Breakpoints
;ArithmeticException
;临时断点是指只触发一次的断点,之后就自动取消了;一般用于特定的场合下需要确认值是符合我们的预期,完了之后就不在需要了;
设置及演示过程如下:
设置方式:
Ctrl + Shift + F8
打开配置窗口;Remove once hit
;设置断点的触发条件,也是阅读源码、修复Bug经常用到的一个功能,比如读Spring源码,研究Bean生命周期的时候,就可以根据Bean的name去设置断点条件,用来判断之后在操作指定对象的时候,才进入断点;
如下示例:
i % 2 == 0
;i == 5000
设置过程:
i % 2 == 0
开发过程中,有时候需要人为制造一些异常,比如事务操作(@Transactional),需要验证是否能达到回滚的效果;
比如下面的伪代码:
// 伪代码 假如这里是个事务操作
// @Transactional
public void save() throws RuntimeException{
table1Save();
// 我先在这里测试一下,异常之后,是否会滚
//throw new RuntimeException("异常了");
table2Save();
table3Save();
}
void table1Save(){}
void table2Save(){}
void table3Save(){}
当咱希望在执行table2Save()
的时候,抛个异常,让整个操作回滚,通常的做法是会在代码中人为抛一个异常:
throw new RuntimeException("异常了");
这样做并没有什么错,但是不是很优雅,而且还存在一下的两个问题:
那有没有什么更好的方式呢?IDEA给我们提供了更加优化的模拟异常方案,不用写异常代码,可以利用工具直接抛出异常,操作步骤如下:
操作步骤:
Throw Execption
;ok
,即可抛出对应的异常;多线程开发的时候,线程的调度策略并不由代码控制,导致断点调试的过程可能会在线程间跳来跳去,如果逻辑复杂点,跳着跳着可能就蒙蔽了;如下示例:
public class Main {
public static void main(String[] args) {
System.out.println("0 main start");
new Thread(() -> {
System.out.println("1 hello");
}, "thread1").start();
new Thread(() -> {
System.out.println("2 world");
}, "thread2").start();
System.out.println("3 main end");
}
}
如果把断点打在System.out.println
上,除了0是能保证第一个输出的,1、2、3的执行顺序是没办法保证的;
默认情况下,断点的suspend
设置是all
,顺序并不固定;
如果将所有断点的suspend
设置为Thread
之后,就会按着线程的顺序,逐个去执行:
在断点的过程中去修改某个变量的值;常见的场景是:当某个变量,因为逻辑bug导致属性值和预期的不一样,但是又不想从头再debug一遍,就可以直接在调试的过程中修改成预期的值,并继续执行后续的步骤;
断点过程中,可以执行一段表达式、代码或者方法
非常实用且特别装B的一个技能,当线上代码出现Bug之后,可以通过此方式用本地代码进行远程调试,快速定位问题并修复;
注意:远程调试必须保证本地代码和线上代码版本一致,否则不会进入断点;
设置步骤如下:
添加一个用于远程调试的接口
@RestController
public class RemoteDebugController {
@GetMapping("debug")
public Integer debug(Integer p){
System.out.println(p);
return p;
}
}
将代码打成jar包
mvn clean package -Dmaven.test.skip=true
IDEA 设置远程调试启动项
以下时几个重要参数的说明
启动项目
为了演示,这里就不在IDEA中启动了,直接在CMD窗口下启动测试项目,记得用上上面生成的参数
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5555 -jar spring-boot-001-hello-world-0.0.1-SNAPSHOT.jar
启动过程出现socket的监听日志,说明正常
Listening for transport dt_socket at address: 5555
IDEA 远程调试配置
测试
左侧为jar包运行的控制台;右上方为IDEA的界面;右下方为浏览器,模拟客户端请求;
当客户端发起请求的时候,IDEA就会进入断点,当执行通过,可以看出,左侧控制台就会打印出对应的日志;
线上调试,务必要给断点加上条件,比如特定测试账号才进去断点;避免让真是用户的请求也进入断点,影响用户的使用;
通过此方式,如果远端的代码有bug,就可以直接在本地的IDEA工具中进行调试,非常的方便;
上面列举了绝大部分常用的Debug功能,但是这并不是所有的,一些不常用功能可以根据需要选择使用
总结,工欲善其事必先利其器,利用好工具,就能做到事半功倍,赶紧收藏,用起来吧!
好了,今天的分享就到这里