IntelliJ IDEA 老司機(jī),還沒用過這 5 個(gè) Intellij IDEA 調(diào)試魔法?
來自:日拱一兵
前言
今天給大家?guī)?5 個(gè)我日常工作以及閱讀源碼必備的 IntelliJ IDEA 高級(jí)調(diào)試技巧,分分鐘要起飛的節(jié)奏
?01?Stream Trace
自從 Java 8 開始,作為程序員的我們都離不開 Stream 相關(guān)功能的使用,書寫起來那叫一個(gè)流暢(這個(gè) feel~~)。但總是有一些時(shí)候,我們對(duì) stream 的操作所要的結(jié)果和預(yù)期不符,這就需要我們逐步調(diào)試,定位問題。
常規(guī)調(diào)試
先來看下面這段代碼:
public?static?void?main(String[]?args)?{
??Object[]?res?=?Stream.of(1,2,3,4,5,6,7,8).filter(?i?->?i%2?==?0).filter(?i?->?i>3).toArray();
??System.out.println(Arrays.toString(res));
}
我們可以在 Stream 操作處打上斷點(diǎn),逐步查看結(jié)果,就像這樣:

我們需要各種單步調(diào)試,不是很直觀,我們迫切的需要個(gè)一覽視圖,讓我們快速查看我們的 Stream 結(jié)果
可視化調(diào)試
同樣先選擇行斷點(diǎn),以?Debug?模式進(jìn)入程序:

接下來會(huì)彈出?Stream Trace,整個(gè) Stream 操作盡顯眼前

同樣可以點(diǎn)擊左下角的?Flat Mode?按鈕,將整個(gè)視圖扁平化

在實(shí)際業(yè)務(wù)中,我們通常對(duì)集合進(jìn)行各種 Stream 操作,我們?cè)賮韨€(gè)復(fù)雜一些的例子:
??List>?customers?=?Arrays.asList(
????Optional.of(new?Customer("日拱一兵",?18)),
????Optional.of(new?Customer("卑微的小開發(fā)",?22)),
????Optional.empty(),
????Optional.of(new?Customer("OOT",?21)),
????Optional.empty(),
????Optional.of(new?Customer("溫柔一刀",?23)),
????Optional.empty()
??);
??long?numberOf65PlusCustomers?=?customers
????.stream()
????.flatMap(c?->?c
??????.map(Stream::of)
??????.orElseGet(Stream::empty))
????.filter(c?->?c.getAge()?>?18)
????.count();
??System.out.println(numberOf65PlusCustomers);
同樣按照上面的操作得到可視化 Stream Trace 視圖,直觀了解整個(gè) Stream 流程,查看對(duì)象屬性等

02 斷點(diǎn)處添加 log
很多程序員在調(diào)試代碼時(shí)都喜歡 print 一些內(nèi)容,這樣看起來更直觀,print 完之后又很容易忘記刪除掉這些沒用的內(nèi)容,最終將代碼提交到 remote,code review 時(shí)又不得不刪減這些內(nèi)容重新提交,不但增加不必要的工作量,還讓 log tree 的一些節(jié)點(diǎn)沒有任何價(jià)值
IntelliJ IDEA 提供 Evaluate and Log at Breakpoints 功能恰巧可以幫助我們解決這個(gè)問題, 來看下面代碼:
public?static?void?main(String[]?args)?{
??ThreadLocalRandom?random?=?ThreadLocalRandom.current();
??int?count?=?0;
??for?(int?i?=?0;?i?5;?i++)?{
???if?(isInterested(random.nextInt(10)))?{
????count++;
???}
??}
??System.out.printf("Found?%d?interested?values%n",?count);
?}
?private?static?boolean?isInterested(int?i)?{
??return?i?%?2?==?0;
?}
假如我們想在第 15 行查看每次調(diào)用,隨即出來的 i 的值到底是多少,我們沒必要在這個(gè)地方添加任何 log,在正常加斷點(diǎn)的地方使用快捷鍵 Shift + 鼠標(biāo)左鍵,就會(huì)彈出下面的內(nèi)容

勾選上 Evaluate and log, 并自定義你想查看的 log/變量,比如這里的 "interested" + i, 這樣以 Debug 模式運(yùn)行程序(正常模式運(yùn)行,不會(huì)打印這些 log):
interested?7
interested?5
interested?1
interested?2
interested?0
Found?2?interested?values
如果你在多處添加了這種斷點(diǎn),簡單的看 log 可能偶爾還是不夠直觀,可以勾選上面圖片綠色框線的 "Breakpoint hit" message :
Breakpoint?reached?at?top.dayarch.TestDebug.isInterested(TestDebug.java:49)
interested?6
Breakpoint?reached?at?top.dayarch.TestDebug.isInterested(TestDebug.java:49)
interested?0
Breakpoint?reached?at?top.dayarch.TestDebug.isInterested(TestDebug.java:49)
interested?9
Breakpoint?reached?at?top.dayarch.TestDebug.isInterested(TestDebug.java:49)
interested?8
Breakpoint?reached?at?top.dayarch.TestDebug.isInterested(TestDebug.java:49)
interested?1
Found?3?interested?values
Disconnected?from?the?target?VM,?address:?'127.0.0.1:0',?transport:?'socket'
Process?finished?with?exit?code?
如果你想要更詳細(xì)的信息,那就勾選上 Stack trace (大家自己查看運(yùn)行結(jié)果吧),有了這個(gè)功能,上面說的一些問題都不復(fù)存在了
03 字段斷點(diǎn)
如果你閱讀源碼,你一定會(huì)有個(gè)困擾,類中的某個(gè)字段的值到底是在哪里改變的,你要一點(diǎn)點(diǎn)追蹤調(diào)用棧,逐步排查,稍不留神,就可能有遺漏
我們可以在 IntelliJ IDEA 中為某個(gè)字段添加斷點(diǎn),當(dāng)字段值有修改時(shí),自動(dòng)跳到相應(yīng)方法位置
使用起來很簡單:
在字段定義處鼠標(biāo)左鍵添加斷點(diǎn)(會(huì)出現(xiàn)「眼睛」的圖標(biāo)) 在「眼睛」圖標(biāo)上鼠標(biāo)右鍵 在彈框中勾選上 Field access和Field modification兩個(gè)選項(xiàng)

如果修改字段值的方法比較多,也可以在 Condition 的地方定義斷點(diǎn)進(jìn)入條件, 有了這個(gè)功能的加成,相信你閱讀源碼會(huì)順暢許多
04 異常斷點(diǎn)
除了閱讀源碼,一定是遇到了異常我們才開始調(diào)試代碼,代碼在拋出異常之后會(huì)自動(dòng)停止,但是我們希望:
代碼停在拋出異常之前,方便我們查看當(dāng)時(shí)的變量信息
這時(shí)我們就用到了 Exception Breakpoints, 當(dāng)拋出異常時(shí),在 catch 的地方打上斷點(diǎn),可以通過下圖的幾個(gè)位置獲取棧頂異常類型,比如這里的 NumberFormatException

知道異常類型后,就可以按照如下步驟添加異常斷點(diǎn)了:

然后在彈框中選擇 NumberFormatException

重新以 Debug 模式運(yùn)行程序:

程序「一路綠燈式」定位到拋出異常的位置,同時(shí)指出當(dāng)時(shí)的變量信息,三個(gè)字:穩(wěn),準(zhǔn),狠,還有誰?
05 方法斷點(diǎn)
當(dāng)閱讀源碼時(shí),比如 Spring,一個(gè)接口的方法可能被多個(gè)子類實(shí)現(xiàn),當(dāng)運(yùn)行時(shí),需要查看調(diào)用棧逐步定位實(shí)現(xiàn)類,IDEA 同樣支持在接口方法上添加斷點(diǎn)(快捷鍵 cmd+F8/ctrl+F8):
鼠標(biāo)左鍵在方法處點(diǎn)擊斷點(diǎn)(??形狀) 斷點(diǎn)上鼠標(biāo)右鍵
勾選上綠色框線上的內(nèi)容,同樣可以自定義跳轉(zhuǎn)條件 Condition

當(dāng)以 Debug 模式運(yùn)行程序的時(shí)候,會(huì)自動(dòng)進(jìn)入實(shí)現(xiàn)類的方法(注意斷點(diǎn)形狀):

看到這你應(yīng)該想到常見的 Runnable 接口中的 run 方法了,同樣是有作用的,大家可以自行去嘗試了
總結(jié)
相信有以上四種調(diào)試技巧的加成,無論是工作 debug 還是閱讀源碼,都可以輕松駕馭了。最后,來看看 IDEA 支持的各種斷點(diǎn)調(diào)試類型。

如有文章對(duì)你有幫助,
“在看”和轉(zhuǎn)發(fā)是對(duì)我最大的支持!
一款牛逼的Java面試題庫,點(diǎn)擊下圖查看詳細(xì)內(nèi)容


