【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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章