Java Debug斷點(diǎn)調(diào)試從入坑到大神(IDEA工具)
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達(dá)
Debug操作技巧
Show Execution Point
將光標(biāo)回到當(dāng)前斷點(diǎn)停頓的地方
Step Over
執(zhí)行當(dāng)前行代碼,并將運(yùn)行進(jìn)度跳轉(zhuǎn)到下一行。
Step Into
進(jìn)入到當(dāng)前代碼行的方法內(nèi)部。

Step Out
從方法內(nèi)部出去

Force Step Into
強(qiáng)制進(jìn)入Java自帶方法的內(nèi)部

Run to Cursor

將光標(biāo)定位到想到達(dá)的代碼行
點(diǎn)擊Run to Cursor
Drop Frame
丟棄當(dāng)前虛擬機(jī)棧幀
初始:
進(jìn)入方法:
丟棄當(dāng)前幀:
也就是說,我們退回了上一步進(jìn)入方法之前。
Evaluate Expression
可以用它來評估表達(dá)式
如 p.getName()等。
Force Return | 避免操作資源
我們在調(diào)試代碼的時候中間出現(xiàn)了異常,但是我們又沒有做異常捕獲,稀里糊涂地把錯誤數(shù)據(jù)存到了數(shù)據(jù)庫中,我們又需要將這些數(shù)據(jù)給刪除,將數(shù)據(jù)庫復(fù)原,才能達(dá)到之前我們需要的效果。
所以,接下來我們講一講如何避免操作資源,強(qiáng)制返回。
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:
我們發(fā)現(xiàn)程序出現(xiàn)了異常
Force Return
它會只打印shit happens,不會繼續(xù)向下執(zhí)行了。
Trace Current Stream Chain | Stream Debug
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);
}




左下角平鋪模式Flat Mode:
斷點(diǎn)常用技巧
斷點(diǎn)(Breakpoint)
斷點(diǎn):如果把程序想象成一條平滑的線,那么斷點(diǎn)就是一個結(jié),可以讓程序中斷在需要的地方,從而方便其分析。
設(shè)置斷點(diǎn):在代碼里需要調(diào)試的地方,鼠標(biāo)雙擊代碼行號的左邊,再次雙擊即可取消斷點(diǎn)。
在調(diào)試中可以設(shè)置的斷點(diǎn)類型有五種:
行斷點(diǎn):
spring在注冊Bean定義(registerBeanDefinition)時,如果是org.springframework.demo.MyBean,就掛起線程,可以開始單步調(diào)試了。
對于命中次數(shù)(hit count)的使用,一般是在循環(huán)中,第N個對象的處理有問題,設(shè)置hit count = N, 重調(diào)試時,可以方便到達(dá)需要調(diào)試的循環(huán)次數(shù)時,停下來調(diào)試。方法斷點(diǎn):
方法斷點(diǎn)的好處是可以從方法方法進(jìn)入或者退出時停下來調(diào)試,類似行斷點(diǎn),而且只有行斷點(diǎn)和方法斷點(diǎn)有條件和訪問次數(shù)的設(shè)置功能。
但是方法斷點(diǎn)還有另外一個好處,如果代碼編譯時,指定不攜帶調(diào)試信息,行斷點(diǎn)是不起作用的,只能打方法斷點(diǎn)。
有興趣的可以將Add line number…前的勾去掉,調(diào)試下看看。觀察斷點(diǎn):
在成員變量上打的斷點(diǎn)。只有對象成員變量有效果,靜態(tài)成員變量不起作用。
可以設(shè)置變量被訪問或者設(shè)置的時候掛起線程/VM。異常斷點(diǎn):
系統(tǒng)發(fā)生異常時,在被捕獲異常的拋出位置處或者程序未捕獲的異常拋出處掛起線程/VM, 也可以指定是否包括異常的子類也被檢測。類加載斷點(diǎn):
在類名上打的斷點(diǎn)。接口上是打不了類加載斷點(diǎn)的,但是抽象類是可以的,只是在調(diào)試的時候,斷點(diǎn)不會明顯進(jìn)入classloader中,單步進(jìn)入知會進(jìn)入到子類的構(gòu)造方法中,非抽象類在掛起線程后單步進(jìn)入就會到classloader中(如果沒有filter過濾掉的話)。類加載斷點(diǎn)不管是打在抽象或者非抽象類上,都會在類第一次加載或者第一個子類第一次被加載時,掛起線程/VM。
注意:每種斷點(diǎn)的設(shè)置有些許不一樣,可以在斷點(diǎn)上右鍵->Breakpoint properties進(jìn)行設(shè)置,但一般在斷點(diǎn)窗口有快速設(shè)置的界面,Breakpoint properties中多了filter, 其實(shí)比較雞肋,用處不大。
調(diào)試狀態(tài)
啟動服務(wù)開始調(diào)試:
方法一:例如上圖的代碼中,鼠標(biāo)點(diǎn)擊main方法-->右鍵Debug As-->Java Application開始java代碼調(diào)試;
方法二:直接點(diǎn)擊“調(diào)試”按鈕,即點(diǎn)擊小瓢蟲邊上的倒三角,選擇Debug As-->Java Application;
方法三:快捷鍵F11;方法四,菜單欄選擇Run-->Debug,還有其他方法此處不再贅述了。
開發(fā)工具首次調(diào)試會彈出提示,需要切換到Debug工作區(qū),勾選“Remember my decision”,下次便不再提示。
調(diào)試執(zhí)行:
| 功能 | 快捷鍵 | 描述 | 備注 |
|---|---|---|---|
| Step Info | F5 | 單步進(jìn)入(如果有方法調(diào)用,將進(jìn)入調(diào)用方法中進(jìn)行調(diào)試) | 逐語句 |
| Step Over | F6 | 單步跳過(不進(jìn)入行的任何方法調(diào)用中,直接執(zhí)行完當(dāng)前代碼行,并跳到下一行) | 逐過程 |
| Step Return | F7 單步返回(執(zhí)行完當(dāng)前方法,并從調(diào)用棧中彈出當(dāng)前方法,返回當(dāng)前方法被調(diào)用處) | 跳出 | |
| Resume | F8 | 恢復(fù)正常執(zhí)行(直到遇到下一個斷點(diǎn)) | 繼續(xù)運(yùn)行 |
| Run to Line | Ctrl+R | 執(zhí)行到當(dāng)前行(將忽略中間所有斷點(diǎn),執(zhí)行到當(dāng)前光標(biāo)所在行) | |
| Drop To Frame | 無 | 回退到指定方法開始處執(zhí)行,這個功能相當(dāng)贊。 在方法調(diào)用棧上的某個方法右鍵,選擇Drop To Frame就可以從該方法的開始處執(zhí)行,比如 重新執(zhí)行本方法,可以在本方法上用Drop To Frame,將從本方法的第一行重新執(zhí)行。 當(dāng)然對于有副作用的方法,比如 數(shù)據(jù)庫操作,更改傳入?yún)?shù)的對象內(nèi)容等操作可能重新執(zhí)行就不再是你想要的內(nèi)容了。 | |
| Copy Stack | 無 | 拷貝當(dāng)前線程棧信息 |
斷點(diǎn)
public class BreakPointDemo {
// 行斷點(diǎn)
public static void line() {
System.out.println("this is the line break point");
}
// 詳細(xì)斷點(diǎn)(源斷點(diǎn))
public static void detailLine() {
System.out.println("this is the detail line break point");
}
// 方法斷點(diǎn) | 接口跳轉(zhuǎn)實(shí)現(xiàn)類
public static void method() {
System.out.println("this is from method");
IService iservice = new IServiceImpl();
iservice.execute();
}
// 異常斷點(diǎn) | 全局捕獲
public static void exception() {
Object o = null;
o.toString();
System.out.println("this line will never be print!");
}
// 字段斷點(diǎn) | 讀寫監(jiān)控
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();
}
}
行斷點(diǎn)
// 行斷點(diǎn)
public static void line() {
System.out.println("this is the line break point");
}
使用鼠標(biāo)左鍵點(diǎn)擊代碼左側(cè):

右鍵點(diǎn)擊行斷點(diǎn),我們也可以進(jìn)行一些斷點(diǎn)停頓的條件設(shè)置:
如 i == 20等條件。
Suspend也可以選擇線程模式,我們可以切換不同的線程,來觀察不同線程的該語句的運(yùn)行效果。(如果是All的話,那就是哪一個線程先過來,那就是哪個線程)
詳細(xì)斷點(diǎn)
// 詳細(xì)斷點(diǎn)(源斷點(diǎn))
public static void detailLine() {
System.out.println("this is the detail line break point");
}SHIFT+鼠標(biāo)左鍵:

debug:
方法斷點(diǎn) | 接口跳轉(zhuǎn)實(shí)現(xiàn)類
方法斷點(diǎn) = 方法起始行斷點(diǎn) + 方法結(jié)尾行斷點(diǎn)
// 方法斷點(diǎn) | 接口跳轉(zhuǎn)實(shí)現(xiàn)類
public static void method() {
System.out.println("this is from method");
IService iservice = new IServiceImpl();
iservice.execute();
}
在方法上打斷點(diǎn):
debug:
第一個斷點(diǎn)停留在方法體內(nèi)第一行代碼:
第二個斷點(diǎn)停留在方法體內(nèi)返回的最后一行代碼:
在接口方法上打斷點(diǎn):
真正運(yùn)行的是接口方法的實(shí)現(xiàn)類:
如果我們有很多的實(shí)現(xiàn)類,我們具體不知道是哪一個,我們只需要在接口方法上打一個斷點(diǎn),它就會自動地跳到接口具體的實(shí)現(xiàn)類方法上。
異常斷點(diǎn) | 全局捕獲
// 異常斷點(diǎn) | 全局捕獲
public static void exception() {
Object o = null;
o.toString();
System.out.println("this line will never be print!");
}
異常斷點(diǎn)會停頓在報出異常的具體代碼行。
點(diǎn)擊View Breakpoints

在異常斷點(diǎn)處添加新的異常斷點(diǎn)




接下來,只要你的程序遇到空指針異常,它就會停頓到發(fā)出空指針異常的那一行代碼那里。
沒有顯式打斷點(diǎn):
debug:
這個異常斷點(diǎn)對于我們異常調(diào)試很方便。
字段斷點(diǎn) | 讀寫監(jiān)控
// 字段斷點(diǎn) | 讀寫監(jiān)控
public static void field() {
Person p = new Person("field", 10);
p.setAge(12);
System.out.println(p);
}
在類的字段屬性上打斷點(diǎn):
我們在字段左邊打了一個字段斷點(diǎn)(小眼睛),它就會去監(jiān)控該字段屬性的整個生命周期的值的變化。
dubug:
第一個:構(gòu)造方法修改了屬性值
第二個:setter方法修改了屬性值
著作權(quán)歸作者所有。
作者:Nemo
鏈接:
https://www.cnblogs.com/blknemo/p/14907523.html
來源:博客園


