Debug功能是我們平時(shí)學(xué)習(xí)或工作中,最常用的IDEA功能之一挣惰∥哉澹可用來追蹤代碼流程,確認(rèn)運(yùn)行過程中參數(shù)變化憎茂,分析定位程序的錯(cuò)誤珍语,線上問題追蹤,也可以用以學(xué)習(xí)第三方框架等竖幔。程序從一個(gè)黑盒板乙,開啟Debug功能后,就變的一絲不掛了拳氢,我們能明白每一步發(fā)生了什么募逞,以及為什么發(fā)生。
看到這里你也許會(huì)說:“Debug有什么難的馋评,點(diǎn)開Debug放接,一直下一步,梭哈到底不就完了嗎留特,費(fèi)那事干嘛纠脾?”那如果,你想回退怎么辦蜕青,或你想更改參數(shù)值怎么辦苟蹈,重新發(fā)一次請(qǐng)求么?再或者需要遠(yuǎn)程調(diào)試時(shí)市咆,你咋辦汉操,這些都是我們接下來要全面學(xué)習(xí)的內(nèi)容。如果能熟練運(yùn)用Debug功能的話蒙兰,一定可以事半功倍磷瘤。
注:開發(fā)工程中,用Debug代替Run搜变,是個(gè)不錯(cuò)的習(xí)慣采缚,可以讓我們隨時(shí)調(diào)試代碼
接下來我會(huì)從這幾個(gè)方面全面向大家介紹一下IDEA的Debug功能。
- 準(zhǔn)備工作
- 主界面介紹
- 各功能鍵詳解
- 查看變量
- 更改變量值
- 設(shè)置斷點(diǎn)條件
- 計(jì)算表達(dá)式
- 回退操作
- 強(qiáng)制返回
- 多線程調(diào)試
- Stream 調(diào)試
- 遠(yuǎn)程Debug
- 最后
準(zhǔn)備工作
開始講解Debug功能之前挠他,我們需要準(zhǔn)備以下工具:
- IDEA
- 調(diào)試代碼
調(diào)試代碼如下扳抽,我們之后的操作,大部分在此代碼上進(jìn)行。此為L(zhǎng)eetcode上的經(jīng)典題目:兩數(shù)之和 的解法贸呢。
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Created By L
* Description:
*/
public class ToSum {
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int sub = target - nums[i];
// 查找余數(shù)是否在表中镰烧,有則返回兩個(gè)數(shù)的索引
// 沒有則將其信息添加進(jìn)哈希表
if (map.containsKey(sub)) {
return new int[]{map.get(sub), i};
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
public static void main(String[] args) {
int[] nums = {2, 7, 11, 15};
int target = 9;
int[] ints = twoSum(nums, target);
System.out.println(Arrays.toString(ints));
}
}
Debug窗口
有同學(xué)的Debug窗口沒有在IDEA底部顯示,可通過雙擊Shift鍵方式打開搜索功能框楞陷,然后輸入Debug怔鳖,選擇如圖第二個(gè)選項(xiàng),Debug窗口即會(huì)出現(xiàn)在底部固蛾。
當(dāng)然结执,右鍵Debug Toolbar 我們還可以選擇,讓窗口出現(xiàn)在上下左右任意位置艾凯。
注:這里用的mac献幔,快捷鍵和Windows不太一樣,不過不影響大家學(xué)習(xí)技能趾诗,我用到的快捷鍵一定是兩平臺(tái)通用的蜡感,然后這里正好強(qiáng)迫下大家去實(shí)際操作一下,學(xué)過沒用過恃泪,等于沒學(xué)過铸敏。
主界面介紹
我們先來看下IDEA中Debug模式下的主界面。
- Debug: 啟動(dòng)Debug功能悟泵。
- 斷點(diǎn): 可在左邊欄單擊,以打上斷點(diǎn)闪水,以Debug方式運(yùn)行到此時(shí)糕非,程序會(huì)停下。
- 服務(wù)按鈕: 可以在這里關(guān)閉服務(wù)球榆、啟動(dòng)服務(wù)朽肥、修改Debug配置,設(shè)置斷點(diǎn)等持钉。
- 調(diào)試按鈕: 共8個(gè)其中包括:Show Execution Point衡招、Step Over (F8)、Step Into (F7)每强、Force Step Into始腾、Step Out、Drop Frame空执、Run to Cursor浪箭、Evaluate Expression,后面會(huì)詳細(xì)解釋功能辨绊。
- Frames: 顯示了該線程調(diào)試所經(jīng)過的所有方法奶栖,勾選右上角的漏斗(Show All Frames)按鈕,就不會(huì)顯示其它類庫的方法了,否則這里會(huì)有一大堆的方法宣鄙。
- Variables: 可以查看變量具體的值袍镀,當(dāng)然也可以修改。
- Watches: 查看變量冻晤,可以將Variables區(qū)中的變量拖到Watches中查看苇羡,默認(rèn)的Debug窗口,可能并沒有這個(gè)窗口明也,你需要點(diǎn)擊Variables窗口的“眼鏡”??圖標(biāo)宣虾,此窗口才會(huì)出現(xiàn)。再點(diǎn)擊Watches列里“眼鏡”??圖標(biāo)温数,即可收回绣硝。
-
Debug窗口: 可以設(shè)置Debug窗口是否固定,或顯示在哪個(gè)邊欄撑刺,只需右鍵即可選擇鹉胖。你甚至可以拖動(dòng)該Debug窗口,使其以獨(dú)立窗口形式存在够傍。
各功能鍵詳解
接下來甫菠,我們依次講講服務(wù)按鈕、調(diào)試按鈕冕屯、Watches操作欄的作用:
服務(wù)按鈕
服務(wù)按鈕如上圖中1區(qū)所示寂诱,從上至下,共10個(gè)按鈕安聘,IDEA版本不同痰洒,可能有小部分不一致。
- Rerun ‘xxx’: 重新運(yùn)行程序浴韭,點(diǎn)擊會(huì)以Debug方式重啟服務(wù)丘喻。
- Edit Run Configuration:“xxx” : 更改運(yùn)行時(shí)配置,其中包括運(yùn)行Java版本更念颈,環(huán)境變量等更改泉粉,還可以用于進(jìn)行遠(yuǎn)程調(diào)試,之后詳解榴芳。
- Resume Program (F9): 恢復(fù)程序嗡靡,從一個(gè)斷點(diǎn)運(yùn)行至下一個(gè)斷點(diǎn),沒有斷點(diǎn)則運(yùn)行完整個(gè)程序翠语。
- Pause Program: 暫停程序叽躯,當(dāng)我們執(zhí)行某步時(shí)間太長(zhǎng),或遇到死循環(huán)時(shí)肌括,可使用其暫停程序点骑,如想繼續(xù)執(zhí)行酣难,再點(diǎn)擊Resume Program (F9)即可。
- Stop ‘xxx’ (Ctrl + F2): 終止當(dāng)前Debug程序黑滴。
- View Breakpoints: 查看所有斷點(diǎn)憨募,還可選擇按包、文件或class類進(jìn)行斷點(diǎn)分組袁辈,還可對(duì)斷點(diǎn)進(jìn)行一些配置菜谣,包括條件配置,多線程配置等晚缩,后面會(huì)詳解尾膊。
- Mute Breakpoints: 沉默所有斷點(diǎn),選擇這個(gè)后荞彼,所有斷點(diǎn)變?yōu)榛疑粤玻瑪帱c(diǎn)失效。再次點(diǎn)擊鸣皂,斷點(diǎn)變?yōu)榧t色抓谴,有效。如果只想使某一個(gè)斷點(diǎn)失效寞缝,可以在斷點(diǎn)上右鍵取消Enabled癌压,會(huì)顯示空心的紅圈,表示當(dāng)前斷點(diǎn)不可用荆陆。
- Get Thread Dump: 導(dǎo)出當(dāng)前堆棧信息滩届,可用作其它分析。
- Settings: 一些顯示設(shè)置被啼,可以設(shè)置是否在代碼界面顯示Values丐吓、Return Values等,開啟即可趟据。
- Pin Tab: 是否固定當(dāng)前Debug標(biāo)簽頁。
調(diào)試按鈕
調(diào)試按鈕如上圖中2區(qū)所示术健,從左至右汹碱,共9個(gè)按鈕,也是我們平時(shí)除服務(wù)按鈕外用的最多的操作了荞估。
- Show Execution Point : 定位咳促,點(diǎn)擊此按鈕可定位到當(dāng)前代碼執(zhí)行的行。
- Step Over (F8): 步過勘伺,一行一行地往下走跪腹,如果執(zhí)行到子方法,子方法內(nèi)又無斷點(diǎn)的情況下飞醉,會(huì)跳過子方法繼續(xù)執(zhí)行冲茸。
- Step Into (F7): 步入,一行一行地往下走,如果執(zhí)行到子方法轴术,無論子方法有無斷點(diǎn)难衰,都會(huì)進(jìn)入到子方法,子方法執(zhí)行完后返回逗栽,繼續(xù)執(zhí)行盖袭。
- Force Step Into (Alt + Shift + F7): 強(qiáng)制步入,能進(jìn)入任何方法彼宠,查看底層源碼的時(shí)候可以用這個(gè)進(jìn)入官方類庫的方法鳄虱。
- Step Out (Shift + F8): 步出,從步入的方法內(nèi)退出到方法調(diào)用處凭峡,此時(shí)方法已執(zhí)行完畢拙已,只是還沒有完成賦值。
- Drop Frame: 回退斷點(diǎn)想罕,相當(dāng)于后悔藥悠栓,后面會(huì)詳解。
- Run to Cursor: 使程序運(yùn)行至光標(biāo)處按价,而光標(biāo)處不需要打斷點(diǎn)惭适,運(yùn)行過程中,有斷點(diǎn)會(huì)在斷點(diǎn)處停止楼镐。
- Evaluate Expression…: 計(jì)算表達(dá)式癞志,后面章節(jié)詳細(xì)說明。
- Trace Current Stream Chain: 顯示Stream處理的整體過程框产。Lambda調(diào)試神器凄杯,之后會(huì)詳解。
Watches操作欄
Watches窗口是觀察變量變化的窗口秉宿,其操作欄如上圖中3區(qū)所示戒突,共6個(gè)按鈕。
- new Watch…: 新增觀察變量描睦。
- Remove Watch: 刪除觀察變量
- Move Watch Up: 向上移動(dòng)觀察變量膊存。
- Move Watch Down: 向下移動(dòng)觀察變量。
- Duplicate Watch: 復(fù)制觀察變量忱叭。
- Show watches in variables tab: Watches不會(huì)默認(rèn)打開為新窗口隔崎,點(diǎn)擊“眼鏡”??圖標(biāo)后,才會(huì)打開為新窗口韵丑,再次點(diǎn)擊會(huì)回到Variables邊欄爵卒。
查看變量
查看變量值的方式有四種,這里我們首先確定我們的服務(wù)按鈕中的Setting中有選擇在代碼界面顯示Values撵彻、Return Values等钓株。如下圖所示即可实牡,沒找到也沒關(guān)系,默認(rèn)都會(huì)顯示的享幽。
在代碼行的后面铲掐,有灰色的變量值顯示。
光標(biāo)懸停到參數(shù)上值桩,也可顯示當(dāng)前變量信息摆霉。如果是復(fù)雜的變量,還可以點(diǎn)開查看詳細(xì)變量值
也可在Variables里查看變量值奔坟,甚至是操作變量值携栋,這里也是變量操作最多的地方。比如咳秉,我們運(yùn)行到一半婉支,想更改某一個(gè)變量的值怎么辦?重新發(fā)一次請(qǐng)求嗎澜建,還是重新運(yùn)行一次向挖?其實(shí)不用這么麻煩,直接更改值即可炕舵!選擇 Set Value… 即可
按下回車何之,當(dāng)前target變量的值就改變了,而后可以繼續(xù)運(yùn)行咽筋。
在Watches也可以觀察變量值溶推,我們可以通過從Variables里拖變量到Watches中觀察,也可以通過點(diǎn)擊 new Watch… 添加觀察變量奸攻。
這里和Variables的區(qū)別是:Variables窗口只會(huì)顯示當(dāng)前代碼行能看到的變量蒜危,Watches只要添加了該變量,則會(huì)一直存在Watches窗口睹耐。
更改變量值
其實(shí)上一節(jié)辐赞,已經(jīng)講了如何在Variables窗口里更改變量值,這里再講一種如何更改變量值硝训,如下圖所示占拍,這里還可以把變量加入Watches窗口中。
比如這里我們這把num[0]改成了22
設(shè)置斷點(diǎn)條件
想像一種情況捎迫,我們要遍歷一個(gè)大集合,我們只有當(dāng)集合為null時(shí)表牢,才停下來窄绒,那我們難道要一次一次的點(diǎn)過去嗎,點(diǎn)到900次的時(shí)候崔兴,你一時(shí)手快彰导,跳過了怎么辦蛔翅?其實(shí)我們可以設(shè)置斷點(diǎn)停留的條件,當(dāng)條件滿足的時(shí)候才停下位谋,否則繼續(xù)運(yùn)行山析。以下方代碼為例:
我們想在值為null值時(shí),停下斷點(diǎn)掏父,看看是哪里為null笋轨,然后去更改數(shù)據(jù)庫,進(jìn)行如下設(shè)置即可赊淑。
只有當(dāng) n == null 這個(gè)條件為空時(shí)爵政,這個(gè)斷點(diǎn)才會(huì)停止!
計(jì)算表達(dá)式
我們?cè)贒ebug時(shí)陶缺,想知道運(yùn)行時(shí)某個(gè)方法的調(diào)用值或表達(dá)式的結(jié)果值钾挟,但是這個(gè)方法又沒寫在代碼中,我們總是會(huì)添加上該方法代碼饱岸,然后重新運(yùn)行一次對(duì)吧掺出。
其實(shí)不用重新運(yùn)行也可以計(jì)算其值,點(diǎn)擊Evaluate Expression…
按鍵即可苫费。比如汤锨,我想知道此時(shí)map.keySet()
的值會(huì)是多少,直接點(diǎn)擊Evaluate即可黍衙。
當(dāng)然我們也可以計(jì)算一些復(fù)雜的表達(dá)式
這里的Evaluate功能其實(shí)就是:用當(dāng)前代碼的上下文環(huán)境泥畅,寫出代碼表達(dá)式,最后計(jì)算出臨時(shí)的結(jié)果值琅翻∥蝗剩可方便我們的各種假設(shè)性代碼實(shí)現(xiàn),而不用每次都去重啟代碼方椎,也不會(huì)影響原代碼的正常運(yùn)行聂抢。
回退操作
回退操作即是Debug中的后悔藥,IDEA中只能回退到上一個(gè)方法調(diào)用處棠众,并不能一步一步回退或是回退到上一個(gè)斷點(diǎn)處琳疏。還有一點(diǎn)值得注意的是,回退只是重新走一下流程闸拿,之前的某些參數(shù)/數(shù)據(jù)的狀態(tài)已經(jīng)改變了的空盼,是無法回退到之前的狀態(tài)的,如對(duì)象新荤、集合揽趾、更新了數(shù)據(jù)庫數(shù)據(jù)等等。
比如苛骨,main方法調(diào)用A方法篱瞎,A方法里刪除了數(shù)據(jù)庫里的一行數(shù)據(jù)苟呐,我們也執(zhí)行了,這時(shí)我們回退俐筋,會(huì)回退到main方法中調(diào)用A方法處牵素,我們A方法中的局部變量的確是回退了,但是數(shù)據(jù)庫里的值已經(jīng)被刪了澄者,回退不了了笆呆,其它第三方對(duì)象、集合原理也類似闷哆,回退不了腰奋。回退的方式很簡(jiǎn)單抱怔,在Frames窗口中劣坊,點(diǎn)擊Drop Frame按鍵即可。
也可一次回退幾個(gè)方法屈留,比如我這里是main調(diào)用a方法局冰,a調(diào)用b方法,b調(diào)用c方法灌危,我這里直接選中a方法Frame康二,右鍵點(diǎn)擊Drop Frame,這樣就直接回退到main方法了勇蝙,右鍵的Drop Frame和操作欄上的效果是一致的沫勿。
強(qiáng)制返回
想象一個(gè)場(chǎng)景,當(dāng)前bug已經(jīng)找到了味混,你不想再繼續(xù)執(zhí)行了产雹,再執(zhí)行后面的代碼會(huì)刪除數(shù)據(jù),你不想執(zhí)行了翁锡,此時(shí)你們?cè)趺崔k蔓挖?直接停掉整個(gè)服務(wù)嗎?能不能只停掉當(dāng)前線程馆衔,而不停掉整個(gè)服務(wù)瘟判?答案是可以的,我們給他一個(gè)返回值角溃。這里使用Force Return
強(qiáng)制返回即可拷获。
比如這里,twoSum方法要返回
int[]
减细,我們直接new int[]{2, 7}
返回即可匆瓜,當(dāng)然我們也可以返回點(diǎn)讓前端開心點(diǎn)的數(shù)據(jù),哄哄前端。或者我們直接拋出異常也是可以的
多線程調(diào)試
Debug默認(rèn)使用時(shí)陕壹,會(huì)阻塞所有線程,只留當(dāng)前線程树埠。平時(shí)如果只是學(xué)習(xí)可能不會(huì)覺得麻煩糠馆,但是開發(fā)中,某個(gè)接口怎憋,其他人在用又碌,難道要?jiǎng)e人等一下,等你Debug完再用嗎绊袋,其實(shí)不用的毕匀。只需要選中如下所示,即可多線程調(diào)試癌别,沒有打斷點(diǎn)的線程會(huì)直接執(zhí)行皂岔,打了斷點(diǎn)的線程,才會(huì)被阻塞展姐,當(dāng)然躁垛,同一方法入口,也可以進(jìn)入多個(gè)線程圾笨,在Frames中進(jìn)行線程的切換即可教馆。
也可選擇 View Breakpoints,然后再選擇Thread開啟多線程調(diào)度模式擂达。
Stream 調(diào)試
有沒有發(fā)現(xiàn)土铺,用Debug調(diào)試Lambda表達(dá)式時(shí)很難受,尤其是哪種一行十幾個(gè)方法哪種板鬓,一寫就是一串悲敷,寫時(shí)爽的一P,Debug時(shí)反向爽的一P穗熬,如果你也有這些問題镀迂,那么接下來這個(gè)神器要好好學(xué)習(xí)一下了。
不知你有沒有發(fā)現(xiàn)唤蔗,調(diào)試按鈕里Trace Current Stream Chain
按鈕常年都是黑色的探遵,為什么呢,因?yàn)樗亲鯯tream調(diào)試的妓柜,只有斷點(diǎn)在Stream表達(dá)式時(shí)才能使用箱季,使用方法即斷點(diǎn)在Stream時(shí),點(diǎn)擊該按鈕即可棍掐。
看出該按鈕的妙處了吧藏雏,能直接把每一步的轉(zhuǎn)換方式或者結(jié)果展示在我們面前,而且還有Split Mode展示方式作煌,能一步一步展示轉(zhuǎn)換結(jié)果掘殴。
遠(yuǎn)程Debug
當(dāng)我們遇到某些問題赚瘦,只能在生產(chǎn)環(huán)境下復(fù)現(xiàn),開發(fā)環(huán)境不能復(fù)現(xiàn)的bug時(shí)奏寨,我們就需要使用遠(yuǎn)程Debug功能了起意。只需要我們本地代碼和遠(yuǎn)程部署的代碼一致即可。當(dāng)然還要在遠(yuǎn)程啟動(dòng)jar包時(shí)病瞳,加入-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=xxx
參數(shù)揽咕,其中xxx為監(jiān)聽端口,詳細(xì)步驟如下:
點(diǎn)擊 Edit Configuration…
依次選擇“+”套菜,然后選擇
Remote JVM Debug
然后填寫一下配置亲善,Name是之后啟動(dòng)的名稱,Host 是遠(yuǎn)程服務(wù)器的 ip逗柴,port: 用于遠(yuǎn)程socket 連接的端口蛹头,注意不能和項(xiàng)目端口一致,否則會(huì)啟動(dòng)失敗嚎于,然后掘而,idea 會(huì)為我們自動(dòng)生成一條命令行參數(shù):
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
接著在遠(yuǎn)程啟動(dòng)jar時(shí),加上我們的參數(shù)
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar xxx.jar
在遠(yuǎn)程項(xiàng)目啟動(dòng)成功后, 在本地以Debug方式運(yùn)行第一步配置的服務(wù)
然后再請(qǐng)求遠(yuǎn)程原服務(wù)于购,我們?cè)诒镜氐臄帱c(diǎn)就起作用了袍睡,是不是很秀