• 【Java】Debug断点调试常用技巧


    Debug操作技巧

    Show Execution Pointimage

    将光标回到当前断点停顿的地方
    image

    Step Overimage

    执行当前行代码,并将运行进度跳转到下一行。

    Step Intoimage

    进入到当前代码行的方法内部。
    image

    image

    Step Outimage

    从方法内部出去
    image

    image

    Force Step Intoimage

    强制进入Java自带方法的内部
    image

    image

    Run to Cursorimage

    image

    将光标定位到想到达的代码行
    image

    点击Run to Cursor
    image

    Drop Frameimage

    丢弃当前虚拟机栈帧

    初始:
    image

    进入方法:
    image

    丢弃当前帧:
    image

    也就是说,我们退回了上一步进入方法之前。

    Evaluate Expressionimage

    可以用它来评估表达式
    image
    如 p.getName()等。
    image

    Force Return | 避免操作资源

    我们在调试代码的时候中间出现了异常,但是我们又没有做异常捕获,稀里糊涂地把错误数据存到了数据库中,我们又需要将这些数据给删除,将数据库复原,才能达到之前我们需要的效果。

    所以,接下来我们讲一讲如何避免操作资源,强制返回。

    public static void saveResource() {
        System.out.println("shit happens");
        
        System.out.println("save to db");
        System.out.println("save to redis");
        System.out.println("send message to mq for money payout");
    }

    debug:
    image

    我们发现程序出现了异常
    image

    Force Return
    image

    它会只打印shit happens,不会继续向下执行了。
    image

    Trace Current Stream Chain | Stream Debugimage

    public static void streamDebug() {
        // stream chain
        Arrays.asList(1, 2, 3, 45).stream()
                .filter(i -> i % 2 == 0 || i % 3 == 0)
                .map(i -> i * i)
                .forEach(System.out::print);
    }

    image

    image

    image

    image

    左下角平铺模式Flat Mode:
    image

    断点常用技巧

    断点(Breakpoint)

    断点:如果把程序想象成一条平滑的线,那么断点就是一个结,可以让程序中断在需要的地方,从而方便其分析。

    设置断点:在代码里需要调试的地方,鼠标双击代码行号的左边,再次双击即可取消断点。

    在调试中可以设置的断点类型有五种:

    • 行断点:
      spring在注册Bean定义(registerBeanDefinition)时,如果是org.springframework.demo.MyBean,就挂起线程,可以开始单步调试了。
      对于命中次数(hit count)的使用,一般是在循环中,第N个对象的处理有问题,设置hit count = N, 重调试时,可以方便到达需要调试的循环次数时,停下来调试。

    • 方法断点:
      方法断点的好处是可以从方法方法进入或者退出时停下来调试,类似行断点,而且只有行断点和方法断点有条件和访问次数的设置功能。
      但是方法断点还有另外一个好处,如果代码编译时,指定不携带调试信息,行断点是不起作用的,只能打方法断点。
      有兴趣的可以将Add line number…前的勾去掉,调试下看看。

    • 观察断点:
      在成员变量上打的断点。只有对象成员变量有效果,静态成员变量不起作用。
      可以设置变量被访问或者设置的时候挂起线程/VM。

    • 异常断点:
      系统发生异常时,在被捕获异常的抛出位置处或者程序未捕获的异常抛出处挂起线程/VM, 也可以指定是否包括异常的子类也被检测。

    • 类加载断点:
      在类名上打的断点。接口上是打不了类加载断点的,但是抽象类是可以的,只是在调试的时候,断点不会明显进入classloader中,单步进入知会进入到子类的构造方法中,非抽象类在挂起线程后单步进入就会到classloader中(如果没有filter过滤掉的话)。类加载断点不管是打在抽象或者非抽象类上,都会在类第一次加载或者第一个子类第一次被加载时,挂起线程/VM。

    注意:每种断点的设置有些许不一样,可以在断点上右键->Breakpoint properties进行设置,但一般在断点窗口有快速设置的界面,Breakpoint properties中多了filter, 其实比较鸡肋,用处不大。

    调试状态

    启动服务开始调试:

    • 方法一:例如上图的代码中,鼠标点击main方法-->右键Debug As-->Java Application开始java代码调试;
    • 方法二:直接点击“调试”按钮,即点击小瓢虫边上的倒三角,选择Debug As-->Java Application;
    • 方法三:快捷键F11;方法四,菜单栏选择Run-->Debug,还有其他方法此处不再赘述了。

      开发工具首次调试会弹出提示,需要切换到Debug工作区,勾选“Remember my decision”,下次便不再提示。

    调试执行:

    功能 快捷键 描述 备注
    Step Info F5 单步进入(如果有方法调用,将进入调用方法中进行调试) 逐语句
    Step Over F6 单步跳过(不进入行的任何方法调用中,直接执行完当前代码行,并跳到下一行) 逐过程
    Step Return F7 单步返回(执行完当前方法,并从调用栈中弹出当前方法,返回当前方法被调用处) 跳出
    Resume F8 恢复正常执行(直到遇到下一个断点) 继续运行
    Run to Line Ctrl+R 执行到当前行(将忽略中间所有断点,执行到当前光标所在行)
    Drop To Frame 回退到指定方法开始处执行,这个功能相当赞。
    在方法调用栈上的某个方法右键,选择Drop To Frame就可以从该方法的开始处执行,比如 重新执行本方法,可以在本方法上用Drop To Frame,将从本方法的第一行重新执行。
    当然对于有副作用的方法,比如 数据库操作,更改传入参数的对象内容等操作可能重新执行就不再是你想要的内容了。
    Copy Stack 拷贝当前线程栈信息

    断点

    public class BreakPointDemo {
        // 行断点
        public static void line() {
            System.out.println("this is the line break point");
        }
        
        // 详细断点(源断点)
        public static void detailLine() {
            System.out.println("this is the detail line break point");
        }
        
        // 方法断点 | 接口跳转实现类
        public static void method() {
            System.out.println("this is from method");
            IService iservice = new IServiceImpl();
            iservice.execute();
        }
        
        // 异常断点 | 全局捕获
        public static void exception() {
            Object o = null;
            o.toString();
            System.out.println("this line will never be print!");
        }
        
        // 字段断点 | 读写监控
        public static void field() {
            Person p = new Person("field", 10);
            p.setAge(12);
            System.out.println(p);
        }
        
        public static void main(String[] args) {
            line();
            detailLine();
            method();
            exception();
            field();
        }
    }

    行断点

        // 行断点
        public static void line() {
            System.out.println("this is the line break point");
        }

    使用鼠标左键点击代码左侧:
    image

    右键点击行断点,我们也可以进行一些断点停顿的条件设置:
    image

    如 i == 20等条件。

    Suspend也可以选择线程模式,我们可以切换不同的线程,来观察不同线程的该语句的运行效果。(如果是All的话,那就是哪一个线程先过来,那就是哪个线程)
    image

    详细断点

        // 详细断点(源断点)
        public static void detailLine() {
            System.out.println("this is the detail line break point");
        }

    SHIFT+鼠标左键:
    image
    image

    debug:
    image

    方法断点 | 接口跳转实现类

    方法断点 = 方法起始行断点 + 方法结尾行断点

        // 方法断点 | 接口跳转实现类
        public static void method() {
            System.out.println("this is from method");
            IService iservice = new IServiceImpl();
            iservice.execute();
        }

    在方法上打断点:
    image

    debug:
    第一个断点停留在方法体内第一行代码:
    image

    第二个断点停留在方法体内返回的最后一行代码:
    image


    在接口方法上打断点:
    image

    真正运行的是接口方法的实现类:
    image

    如果我们有很多的实现类,我们具体不知道是哪一个,我们只需要在接口方法上打一个断点,它就会自动地跳到接口具体的实现类方法上。

    异常断点 | 全局捕获

        // 异常断点 | 全局捕获
        public static void exception() {
            Object o = null;
            o.toString();
            System.out.println("this line will never be print!");
        }

    异常断点会停顿在报出异常的具体代码行。

    1. 点击View Breakpoints
      image

    2. 在异常断点处添加新的异常断点
      image
      image
      image
      image

    3. 接下来,只要你的程序遇到空指针异常,它就会停顿到发出空指针异常的那一行代码那里。

    没有显式打断点:
    image

    debug:
    image

    这个异常断点对于我们异常调试很方便。

    字段断点 | 读写监控

        // 字段断点 | 读写监控
        public static void field() {
            Person p = new Person("field", 10);
            p.setAge(12);
            System.out.println(p);
        }

    在类的字段属性上打断点:
    image

    我们在字段左边打了一个字段断点(小眼睛),它就会去监控该字段属性的整个生命周期的值的变化。

    dubug:
    第一个:构造方法修改了属性值
    image

    第二个:setter方法修改了属性值
    image

    远程断点调试 Debug

    远程断点调试流程

    image

    正常情况下:

    1. 用户访问服务器ip地址
    2. 服务器返回结果

    远程断点调试Debug情况下:

    1. 用户访问服务器
    2. 服务器得到了具体运行的那一行
    3. 然后就会去问一下本地跟它连接的IDEA,问它有没有这一行的断点
    4. IDEA检查了一下自己的断点列表
    5. 然后开发人员就开始debug调试
    6. 结束调试,将结束调试的信息返回给服务器
    7. 服务器返回运行结果给用户

    实例

    代码:

    @SpringBootApplication
    @RestController
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
        
        // 远程服务器地址 192.168.31.224
        @RequestMapping("/{name}")
        public String hello(@PathVariable("name") String name) {
            System.out.println("debug is running");
            return "hello world" + name;
        }
    }

    点击image

    点击image

    选择Attach to remote JVM(依附上远程服务器的端口)
    image

    填写ip地址
    image

    可以看到下面生成了命令行参数,在服务器端使用java -Xdebug xxxx -jar demo.jarxxxx为命令行参数(注意 * 号要进行转义处理),这样服务器就启动了
    image

    运行Debug程序
    image
    就可以正常Debug了


    问题:如果我们把本地的代码修改了之后,服务器返回给用户的信息会改变吗?
    回答:不会改变,虽然说我们把本地的代码改了,但是我们本地的修改没有部署到线上去,所以我们程序运行的逻辑还是按照线上来运行的

    后海有树的院子,夏代有工的玉,此时此刻的云,二十来岁的你。——《可遇不可求的事》

    笔者将不定期更新【考研或就业】的专业相关知识以及自身理解,希望大家能【关注】我。
    如果觉得对您有用,请点击左下角的【点赞】按钮,给我一些鼓励,谢谢!
    如果有更好的理解或建议,请在【评论】中写出,我会及时修改,谢谢啦!
    关注
    评论
    收藏
    Top
  • 相关阅读:
    大爆炸集成 Big Bang
    How:怎么做集成测试
    Where:对什么部分做集成测试
    web预览图片
    对象的属性赋值
    Web请求
    Sql Server 获取数据库最近执行的操作
    服务安装脚本
    生成并保存二维码图片,此方法最清晰
    c# get,set设置默认值
  • 原文地址:https://www.cnblogs.com/blknemo/p/14907523.html
Copyright © 2020-2023  润新知