大家好,我是一航!
寫Bug、改Bug幾乎占據了程式設計師日常工作的絕大部份時間,如果你能掌握一手偵錯程式碼的絕技,相信工作效率必定會得到大振幅的提升;
IDEA 就為我們提供了很多簡單且非常強大的偵錯功能,但是發現有些小夥伴並沒有完全用上,今天就一起來玩一下那些Debug的 奇淫巧計 ;30來個功能及偵錯小技巧,學完後讓你開發偵錯效率提升個10倍!爭取做到早上9點上班,10點就能下班 (小聲說:是晚上10點【手動狗頭】)
本文的目錄:
IDEA的Debug控制台在整個視窗的左下方;
IDEA 偵錯功能說明
Show Execution Point
快捷鍵: Alt + F10
回到當前啟用的斷點處 ;當你的滑鼠不在斷點所處的行,點選之後,會立馬復位到斷點處;
Step Ove
快捷鍵: F8
單步偵錯;逐行往下執行,如果執行行有其他方法,不會進入對應的方法;日常Debug用的最多的一個功能
Step Into
快捷鍵: F7
進入方法體內部。該功能會進入自訂的方法或者三方庫的方法;註意, 不會進入JDK的方法 ;
Force Step Into
快捷鍵: Alt + Shift + F7
強制進入方法體內部,與
Step Into
不同的是,會進入JDK的方法;
Step Out
快捷鍵: Shift + F8
跳出方法體;一般會配合
(Force)Step Into
一起使用
Drop frame
回到方法的呼叫處,同時上下文內所有的變量的值也回到那個時候。
該按鈕能夠點選的前提條件是: 當前所處的方法有上級方法 ,如果你是main方法裏,那麽按鈕就是灰色,無法點選;
Run to Cursor
快捷鍵: Alt + F9
將程式碼執行到光標處,光標停在哪裏就執行到哪裏;
Evaluate Expression
快捷鍵: Alt + F8
運算式小算盤;可執行任意合法的運算式。
Trace Current Stream Chain
追蹤當前Stream流;只有在Stream程式碼上,此按鈕才會亮起。
Rerun Main
快捷鍵: Ctrl + F5
查詢執行Debug;
Resume Program
快捷鍵: F9
恢復程式;當因為斷點導致程式碼停止之後,此功能可以讓持續恢復執行;有下一個斷點時,會跳轉到下一個斷點;沒有下一個斷點,會執行到持續結束;
Stop Main
快捷鍵: Ctrl + F12
停止程式;
View Breakpoints
快捷鍵: Ctrl + Shift + F8
開啟斷點管理視窗;
Mute Breakpoints
停用所有的斷點;
Get Thread Dump
拿到當前執行緒的Dump,可以檢視當前執行緒的狀態;
篩選
偵錯小技巧
行斷點
行斷點的圖示是一個 圓形的紅點,在需要斷點的程式碼行頭點選,即可添加斷點
方法斷點
將斷點打在某個具體的方法上,方法執行的時候,會進入斷點;
舉個方法偵錯最常用的Debug場景:
當閱讀源碼或者自己寫業務需求的時候,經常會用到策略、樣版方法等設計模式;在偵錯的時候,需要知道,當前介面方法或者抽象方法的執行,到底是走的哪一個具體的實作,用 方法偵錯 就能很方便的找到;如下範例;
介面
Service
有兩個具體的實作:
ServiceA
和
ServiceB
,分別實作了介面的
method
方法,偵錯的過程中就可以將斷點打在介面的method方法上;當我們在Main方法中例項化了ServiceB,斷點就自動進入到ServiceB的method()方法了;
介面Service
publicinterfaceService{
voidmethod();
}
ServiceA
public classServiceAimplementsService{
@Override
publicvoidmethod(){
System.out.println("Service A");
}
}
ServiceB
public classServiceBimplementsService{
@Override
publicvoidmethod(){
System.out.println("Service B");
}
}
Main
public classMain{
publicstaticvoidmain(String[] args){
Service serviceB = new ServiceB();
serviceB.method();
}
}
更多功能點
Condition
用於輸入運算式,進行過濾
Watch
Method entry 和 Method exit 至少有一個選項存在。
Emulated
用於提高偵錯效能
Method entry
進入方法時啟用斷點
Method exit
結束方法時啟用斷點
內容斷點
在內容的行頭點選即可添加一個小眼睛一樣的內容監聽斷點,用於監聽某個內容的讀寫變化過程;
更多功能點
Condition
用於輸入運算式,進行過濾
Watch
Filed Access
讀取此內容時(寫入時不管)
Filed modification
寫入此內容時(讀取時不管)
異常斷點
異常斷點是開發、偵錯的時候經常用到的一個功能,用於快速定位到那行程式碼出現了異常;
設定方式:
第一步,使用快捷鍵
Ctrl + Shift + F8
開啟配置視窗;
第二步,點選左上角的
+
號;
第三步,選擇
Java Exception Breakpoints
;
第四步,添加需要斷點的異常,如:
ArithmeticException
;
第五步,Debug執行,當出現指定的異常時,就會進入斷點;
更多功能點
Condition
用於輸入運算式,進行過濾
Watch
Caught excetion
只有當你自己try-catch了這個異常才會啟用斷點
Uncaught excetion
只有當你自己不try-catch時才會啟用斷點
臨時斷點
臨時斷點是指 只觸發一次的斷點 ,之後就自動取消了;一般用於特定的場合下需要確認值是符合我們的預期,完了之後就不在需要了;
設定及演示過程如下:
設定方式:
第一步,設定斷點
第二步,使用快捷鍵
Ctrl + Shift + F8
開啟配置視窗;
第三步,找到對應的斷點;
第四步,勾選
Remove once hit
;
第五步,Debug執行,當斷點觸發一次之後,就自動取消了;
斷點條件
設定斷點的觸發條件,也是閱讀源碼、修復Bug經常用到的一個功能,比如讀Spring源碼,研究Bean生命周期的時候,就可以根據Bean的name去設定斷點條件,用來判斷之後在操作指定物件的時候,才進入斷點;
如下範例:
for迴圈之後只有i是2的倍數時,才進入斷點,可以在Conditon中填入
i % 2 == 0
;0-10000的迴圈,當i等於5000的時候,進入斷點,其他的時候忽略,可以在Conditon中填入
i == 5000
設定過程:
第一步,設定斷點
第二步,右鍵斷點處,開啟操作界面
第三步,輸入運算式,比如迴圈時只有偶數才斷點,可以輸入
i % 2 == 0
模擬異常
開發過程中,有時候需要人為制造一些異常,比如事務操作(@Transactional),需要驗證是否能達到回滾的效果;
比如下面的虛擬碼:
// 虛擬碼 假如這裏是個事務操作
// @Transactional
publicvoidsave()throws RuntimeException{
table1Save();
// 我先在這裏測試一下,異常之後,是否會滾
//throw new RuntimeException("異常了");
table2Save();
table3Save();
}
voidtable1Save(){}
voidtable2Save(){}
voidtable3Save(){}
當咱希望在執行
table2Save()
的時候,拋個異常,讓整個操作回滾,通常的做法是會在程式碼中人為拋一個異常:
thrownew RuntimeException("異常了");
這樣做並沒有什麽錯,但是不是很優雅,而且還存在一下的兩個問題:
只能在方法的末尾拋異常;流程中間拋,後面的程式碼會報錯
有風險
這種業務功能中人為拋異常,如果一不小心忘記刪除,將這個異常送出上去,就是人為的生產事故,可能帶來比較嚴重的後果;
IDEA優雅模擬異常
那有沒有什麽更好的方式呢?IDEA給我們提供了更加最佳化的模擬異常方案,不用寫異常程式碼,可以利用工具直接丟擲異常,操作步驟如下:
操作步驟:
第一步,在要模擬異常的地方加上斷點;
第二步,Debug模式執行程式碼並進入斷點;
第三步,Frames中找到對應的斷點記錄;
第四步,右鍵,選擇
Throw Execption
;
第五步,輸入你想丟擲的異常,點選
ok
,即可丟擲對應的異常;
多執行緒偵錯
多執行緒開發的時候,執行緒的排程策略並不由程式碼控制,導致斷點偵錯的過程可能會線上程間跳來跳去,如果邏輯復雜點,跳著跳著可能就蒙蔽了;如下範例:
public classMain{
publicstaticvoidmain(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 classRemoteDebugController{
@GetMapping("debug")
public Integer debug(Integer p){
System.out.println(p);
return p;
}
}
將程式碼打成jar包
mvn clean package -Dmaven.test.skip=true
IDEA 設定遠端偵錯啟動項
以下時幾個重要參數的說明
Name
名稱,不重要,隨意填寫;
Host
遠端服務所處的機器IP,這裏是本機測試,所以填寫127.0.0.1即可,實際使用填寫遠端服務所處的真實IP
PORT
遠端偵錯的埠
遠端服務執行時的JVM參數
IDEA 工具幫我們生成的服務執行時需要添加的JVM參數,直接復制使用即可;
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5555
啟動計畫
為了演示,這裏就不在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功能,但是這並不是所有的,一些不常用功能可以根據需要選擇使用
總結,工欲善其事必先利其器,利用好工具,就能做到事半功倍,趕緊收藏,用起來吧!
好了,今天的分享就到這裏,感謝大家的點贊、轉發、收藏!
>>
END
精品資料,超贊福利,免費領
微信掃碼/長按辨識 添加【技術交流群】
群內每天分享精品學習資料
最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎、並行、JVM、MySQL、Redis、Spring、SpringMVC、SpringBoot、SpringCloud、訊息佇列等多個型別),歡迎您的使用。
👇👇
👇點選"閱讀原文",獲取更多資料(持續更新中)