IntelliJ IDEA 老司機,還沒用過這 5 個 Intellij IDEA 調試魔法?
來自:日拱一兵
前言
今天給大家?guī)?5 個我日常工作以及閱讀源碼必備的 IntelliJ IDEA 高級調試技巧,分分鐘要起飛的節(jié)奏
?01?Stream Trace
自從 Java 8 開始,作為程序員的我們都離不開 Stream 相關功能的使用,書寫起來那叫一個流暢(這個 feel~~)。但總是有一些時候,我們對 stream 的操作所要的結果和預期不符,這就需要我們逐步調試,定位問題。
常規(guī)調試
先來看下面這段代碼:
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 操作處打上斷點,逐步查看結果,就像這樣:

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

接下來會彈出?Stream Trace,整個 Stream 操作盡顯眼前

同樣可以點擊左下角的?Flat Mode?按鈕,將整個視圖扁平化

在實際業(yè)務中,我們通常對集合進行各種 Stream 操作,我們再來個復雜一些的例子:
??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 視圖,直觀了解整個 Stream 流程,查看對象屬性等

02 斷點處添加 log
很多程序員在調試代碼時都喜歡 print 一些內容,這樣看起來更直觀,print 完之后又很容易忘記刪除掉這些沒用的內容,最終將代碼提交到 remote,code review 時又不得不刪減這些內容重新提交,不但增加不必要的工作量,還讓 log tree 的一些節(jié)點沒有任何價值
IntelliJ IDEA 提供 Evaluate and Log at Breakpoints 功能恰巧可以幫助我們解決這個問題, 來看下面代碼:
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 行查看每次調用,隨即出來的 i 的值到底是多少,我們沒必要在這個地方添加任何 log,在正常加斷點的地方使用快捷鍵 Shift + 鼠標左鍵,就會彈出下面的內容

勾選上 Evaluate and log, 并自定義你想查看的 log/變量,比如這里的 "interested" + i, 這樣以 Debug 模式運行程序(正常模式運行,不會打印這些 log):
interested?7
interested?5
interested?1
interested?2
interested?0
Found?2?interested?values
如果你在多處添加了這種斷點,簡單的看 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?
如果你想要更詳細的信息,那就勾選上 Stack trace (大家自己查看運行結果吧),有了這個功能,上面說的一些問題都不復存在了
03 字段斷點
如果你閱讀源碼,你一定會有個困擾,類中的某個字段的值到底是在哪里改變的,你要一點點追蹤調用棧,逐步排查,稍不留神,就可能有遺漏
我們可以在 IntelliJ IDEA 中為某個字段添加斷點,當字段值有修改時,自動跳到相應方法位置
使用起來很簡單:
在字段定義處鼠標左鍵添加斷點(會出現「眼睛」的圖標) 在「眼睛」圖標上鼠標右鍵 在彈框中勾選上 Field access和Field modification兩個選項

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

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

然后在彈框中選擇 NumberFormatException

重新以 Debug 模式運行程序:

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

當以 Debug 模式運行程序的時候,會自動進入實現類的方法(注意斷點形狀):

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

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


