收藏!這些IDE使用技巧,你都知道嗎

一 背景

1 目的

欲善其事,先利其器。對于研發同學,在日常的開發工作中,我們與之打交道最多的便是編程的IDE。能否高效和靈活的使用IDE,將對我們的工作效率起著舉足輕重的作用。

研發同學在開發中最主要做的兩件事分別是架構設計和編碼,前者主要取決于大量的項目經驗積累和個人的思考深度,也是作為研發的核心競爭力,短時間內很難快速求成;后者主要取決于日常的編碼練習和一定程度的IDE信息差,能夠通過下文中介紹的一系列技巧進行能力的快速補齊和鞏固加強。

本文的主要目的有兩方面:

一方面,對IDE的快捷操作和高效技巧,結合自己多年的實踐和理解,進行一次系統性的總結和梳理。

另一方面,希望通過本文系統性的梳理,能夠幫助更多的同學提高研發效率,無論你是剛入手不久的新人,還是有著多年開發經驗的專家,相信你都能夠在本文中發現一片新天地,讓你能夠有更多的時間和精力去做更有意義的事情。

2 定位

網上很多技術網站和個人博客,對于IDE各種技巧和便捷操作總結得非常具體且詳細,對于單點的詳盡程度都是極具參考和學習價值的。但其對應的問題是,這些很多很優秀的文章,出自于不同的手筆,有各自的行文風格,且分散在各個網站的散點,難以系統化。

我對本文的定位是,將各種技巧以大分類的形式進行收攏和聚合,以幫助大家構建和完善整體的知識體系,大幅度提高開發效率。對于每個分類點到即止,替代咀嚼式灌輸方式的是,盡量使用漸進式引導的方式。

3 普適性

JetBrains系列的IDE產品眾多,除了下圖之外,還有其他未列入的,如Google二次開發的Android Studio等。雖然歸為多個產品實例,但這些IDE的內核都是一樣的,只是在內核的基礎上額外添加了各自的語言特性。本文將以使用量最高的一款IDE——IDEA為例進行展開,文中提到的絕大多數能力和技巧,在其他IDE均同樣適用,一通則百通。

image.png

二 Postfix Completion

1 介紹

Postfix Completion (下稱Postfix) 是一種通過 . + 模板Key 來對當前已經輸出的表達式,添加和應用預設代碼模板的編碼增強能力。

其核心要解決的問題是,將編碼過程中一些通用的代碼結構范式進行抽象和沉淀,并能在同類型的場景下,通過 . + 模板Key 的方式進行喚醒和復用。

舉個例子,現在需要完成下面一段代碼的編寫,為了對name參數進行判空保護:

if(name!=null){}

在普通文本編輯器中,其中 if 2次, name 4次, (){}!= 共6次,再加空格Tab和光標切換,一共需要按鍵23次。

在IDEA編輯器中,不使用Postfix時,一共需要按鍵20次,不考慮代碼格式化的情況可以減少到16次。

在IDEA編輯器中,使用Postfix時,只需要8次,如下圖:

image

在這個例子中,可以對比出使用Postfix前后的效果,使用之后在編碼中減少了一半的手動按鍵操作,且生成的代碼是自帶格式化的。在實際的編碼過程中,各項目大小和復雜度差異性雖然很大,但細化到這種基本單位的編程范式時,它們都是融會貫通的。

與上例中nn并列的Postfix,IDEA給我們預設的還有很多,下面對一些非常高頻使用的Postfix進行梳理。

2 梳理

var

快速定義一個局部變量,自帶IDE的類型推斷

image

notnull

快速進行NPE的判空保護:

image

nn

同notnull,是它的簡寫,推薦用這個,更加便捷:

??????

try catch

快速對當前語句添加try catch異常捕獲,同時IDE還會對catch中的Exception自動做類型推斷:

image

cast

快速實現類型強轉,不需要反復使用()包裹和光標切換;配合instanceof使用時還能自動實現cast類型的推斷:

??????

if

快速實現if判斷的代碼范式:

image

throw

快速實現拋異常:

??????

for

快速實現集合或數組的迭代:

??????

fori

快速實現集合或數組的帶索引值迭代;同時對整型數字也支持:

image

sout/soutv

快速實現(不帶參數/帶參數)的打印功能:

image

return

快速實現方法中的值返回邏輯:

image

format

快速實現字符串格式化:

image

3 高級用法

擔心系統預設的Postfix不足以滿足我們的編碼需求,IDEA還提供了Postfix的自定義功能。

這里我以自定義一個對集合判空的代碼范式,來舉例說明自定義Postfix的流程:

1)進入IDE設置界面,然后依次進入Editor => General => Postfix Completion => 面板左下角加號 => Java:

image

2)在彈起的頁面中,按照下圖進行配置,然后保存退出設置頁。

image.png

此時我們自定義的 isempty 這個Postfix即完成了,下面來看下實際使用的效果:

??????

在實際開發過程中,對于根據已經輸入的表達式就能決定接下來代碼格式的功能,我們都能使用這種自定義方式進行代碼的抽象和復用。

接下來介紹IDE中一種跟Postfix功能很相像,但靈活度更高的能力——Live Template。

三 Live Template

1 介紹

介紹之前可以先看一段簡短的編碼過程:

image

上面這段編碼中,我先后使用了Live Template的以下三個模板能力:

psfs :定義字符串常量

main :添加入口函數

sout :實現日志輸出

這里我們將其和上面提到的Postfix對比來看,兩者都是提供代碼級別模板的功能。不同的是,Postfix需要一個已經輸入的表達式和 . + 模板Key 來進行觸發,而Live Template不需要這些,它僅僅需要 模板Key 即可觸發。

Live Template提供的預設模板要比Postfix要高出一個數量級,因此這里我就不進行一一演示,我們可以進行設置面板,然后按照Editor => Live Templates的路徑自行查看,如下圖:

image

2 高級用法

和Postfix一樣,Live Template也支持自定義模板,但它的自定義模板相對來說更加靈活和開放,甚至支持我們直接植入腳本。鑒于Live Template的高度靈活性,單獨介紹這塊會占據大量的篇幅,因此這里我將從幾個實際的案例場景來開拓一下思路,而具體自定義拓展過程就不詳細展開介紹了。

Key值映射

將DB中查詢到 List<T> 結構的數據,根據Key值映射轉化為 Map<K, T> 結構的數據,以便于進行后續的數據填充邏輯:

image

DB批量查詢

在數據查詢時,我們會有根據ID組件進行批量DB數據查詢的訴求,如下:

List<User>users=userMapper.queryUserByIds(userIds);

這種寫法會有一個弊端,就是當 userIds 達到一定的量級時,該查詢會變得非常耗時。

對于該問題其中一個解法是,將這個大的 userIds 拆分成多個批次,然后讓這多個批次異步并行去查詢。這里便使用Live Template來抽取一個針對該場景的代碼模板,如下:

??????

按照該模板,我們的查詢語句將變成這樣:

List<User>users=batchQuery(userIds,100,userMapper::queryUserByIds,null);

可以看到,和之前相比,多傳一個分批的size參數,同時還支持指定的異步任務調度器的自定義配置,而返回結果和之前的查詢方式保持完全一致,不需要外部有額外的適配工作。

腳本植入

這個功能是我非常看好Live Template的主要原因,它的靈活性和拓展性也主要來源于這里。它支持我們通過一個 模板Key 來喚起和執行一段腳本,這也就意味著,我們的自定義的Live Template模板是可編程的,極大程度提高了該模板的拓展性。

單描述功能會有些空洞,這里我結合一個實際案例進行介紹,我們來實現一個跨電腦的代碼共享功能:

1)首先,使用python的flask框架寫一個極簡的服務端應用并啟動,提供最簡單的 push 和 pull的能力,如下:

fromflaskimportFlask,requestDEFAULT='nothing'code=DEFAULTapp=Flask(__name__)@app.route('/push')defpush():globalcode? code=request.args.get('code',DEFAULT)return'Success'@app.route('/pull')defpull():returncodeapp.run()

2)然后,我們來通過groovy腳本實現一個代碼 pull 的模板,這里應用了Live Template的 groovy script 能力,對應腳本如下:

defurl=newURL('http://127.0.0.1:5000/pull');defconn=url.openConnection()asHttpURLConnection;defresult=conn.inputStream.text;returnresult

3)最后,再實現代碼push的模板,腳本如下(下面的代碼入參,是通過剪切板賦值傳遞過來的):

defcode=_1;defurl=newURL('http://127.0.0.1:5000/push?code='+newURLEncoder().encode(code));defconn=url.openConnection()asHttpURLConnection;defresult=conn.inputStream.text;returnresult

此時就已經完成了跨設備的代碼分享功能,為方便演示,這里就用 People1 和 People2 兩個類來模擬兩臺獨立的電腦。 People1 將自己的一段代碼復制到剪切板中,然后通過 push 模板調用 push 接口來將這段代碼上傳到Python服務應用中; People2 再通過 pull 腳本來調用服務端的 pull接口,訪問到 People1 上傳的代碼并輸入到當前的代碼編輯器中,實現效果如下圖:

image

這里的代碼共享只是一個引子,除此之外,我們還能寫很多有意思的腳本,比如在IDE中查天氣、通過IDE聊天等等,自行腦補拓展。

介紹完Live Template之后,接下來介紹文件級別的模板——File Template。

四 File Template

1 介紹

File Template,顧名思義,對應文件級別的模板。對于該模板,我們使用腳本的主要在于兩個場景,分別是文件頭和文件的自定義,下面結合案例依次展開。

2 自定義文件頭

按照下圖的路徑,來更改文件頭的格式,IDE就會在我們新建一個類或接口時,根據這里的配置格式來自動生成對應的文件注釋頭。

image

3 抽象通用Controller

看下面一段代碼,這是一個針對于User這個domain的增刪改查接口類:

packagecom.alibaba.ide.code.controller;importcom.alibaba.ide.code.entity.Result;importcom.alibaba.ide.code.entity.User;importcom.alibaba.ide.code.service.Condition;importcom.alibaba.ide.code.service.UserService;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;importjava.io.Serializable;importjava.util.List;/**

* @author puke

* @version 2021/2/9

*/@RestController@RequestMapping("api/user")publicclassUserController{@ResourceprivateUserService userService;@PostMappingpublicResult<User>create(@RequestBodyUser record){User user=userService.insert(record);returnResult.success(user);}@PutMappingpublicResult<User>update(@RequestBodyUser record){User user=userService.update(record);returnResult.success(user);}@DeleteMapping("{id}")publicResult<Void>deleteById(@PathVariableSerializable id){boolean success=userService.deleteById(id);returnsuccess?Result.success():Result.fail();}@GetMapping("{id}")publicResult<User>queryById(@PathVariableSerializable id){User user=userService.queryById(id);returnResult.success(user);}@GetMappingpublicResult<List<User>>queryByCondition(Condition<User>condition){List<User>list=userService.queryByCondition(condition);returnResult.success(list);}}

仔細看這段代碼會發現,如果基于該接口再新增另一個domain對應的Controller接口類,代碼中的基本結構和邏輯都是可以復用的。此時,便是File Template排上用場的地方,我們定義一個通用的 Controller 模板,將共性的部分抽象到模板里,再將差異性的部分通過模板入參 Subject 變量傳入進來(注,這里需要用到Velocity 模板[1]的知識)。

#set($SubjectOfLowerFirst=${Subject.substring(0,1).toLowerCase()}+$Subject.substring(1))package${PACKAGE_NAME};importcom.alibaba.ide.code.entity.Result;importcom.alibaba.ide.code.entity.${Subject};importcom.alibaba.ide.code.service.Condition;importcom.alibaba.ide.code.service.${Subject}Service;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;importjava.io.Serializable;importjava.util.List;#parse("File Header.java")@RestController@RequestMapping("api/${SubjectOfLowerFirst}")publicclass${Subject}Controller{@Resourceprivate${Subject}Service ${SubjectOfLowerFirst}Service;@PostMappingpublicResult<${Subject}>create(@RequestBody${Subject}record){${Subject}${SubjectOfLowerFirst}=${SubjectOfLowerFirst}Service.insert(record);returnResult.success(${SubjectOfLowerFirst});}@PutMappingpublicResult<${Subject}>update(@RequestBody${Subject}record){${Subject}${SubjectOfLowerFirst}=${SubjectOfLowerFirst}Service.update(record);returnResult.success(${SubjectOfLowerFirst});}@DeleteMapping("{id}")publicResult<Void>deleteById(@PathVariableSerializable id){boolean success=${SubjectOfLowerFirst}Service.deleteById(id);returnsuccess?Result.success():Result.fail();}@GetMapping("{id}")publicResult<${Subject}>queryById(@PathVariableSerializable id){${Subject}${SubjectOfLowerFirst}=${SubjectOfLowerFirst}Service.queryById(id);returnResult.success(${SubjectOfLowerFirst});}@GetMappingpublicResult<List<${Subject}>>queryByCondition(Condition<${Subject}>condition){List<${Subject}>list=${SubjectOfLowerFirst}Service.queryByCondition(condition);returnResult.success(list);}}

模板定義完成,接下來來看一下實際的使用效果:

image

這里使用 Goods 作為新的domain對象,可以看到,生成的 Controller 代碼已經具備 UserController 的全部能力,并且生成的代碼全部都是 Goods 相關的api,這樣就實現了File Template的橫向遷移能力。

五 低頻高效快捷鍵

1 介紹

IDEA中的快捷鍵多達上百個,我們很難把每個都記清楚,網上也有很多對應的總結。這里我主要梳理一些,大家使用相對比較低頻,但又非常高效的快捷鍵。

2 梳理

選擇重復元素:Control + G

通常情況下,我們可以使用 Shift + F6 對類名、方法名和變量名進行批量更改,但對于其他元素進行批量更改時,該快捷鍵特別合適,且不限編程語言。

??????

批量框選:Option + 鼠標左鍵拖拽

對于"對齊"的代碼進行批量更改的最優解,沒有之一:

image

整行移動:Option + Shift + ↑/↓

快速調整代碼執行順序,免除繁瑣的剪切粘貼過程:

image

整行/塊復制:Command + D

對于整行/塊的復制,效率遠高于純手動的復制粘貼:

image

展開/收起:Command + . or Command + Shift + +/-

前者,快速顯示/隱藏當前方法體;后者,快速概覽當前類的所有方法:

image

修改方法簽名:Command + F6

該方法被多文件或多處調用時,該方式替換效率極高:

image

查看歷史剪切板:Command + Shift + V

開發中經常會出現需要復制多個文本的訴求,而PC默認的剪切板只能保存一個,該功能專門用來解決這個痛點:

image

代碼抽取

代碼抽取主要用在代碼重構的時候,以最快速度達到我們抽取一個變量、方法的目的。

1)抽局部變量:Command + Option + V

image

2)抽成員變量:Command + Option + F

image

3)抽靜態常量:Command + Option + C

image

4)抽方法入參:Command + Option + P

image

5)抽方法:Command + Option + M

??????

六 代碼調試

代碼調試在開發中使用的非常多,常規的單步、多步、進入、跳出操作這里也不特殊說明了。

有一點值得說的就是,利用條件斷點來實現運行期的代碼植入功能,先看下圖:

??????

可以看到,Debug模式運行時,我們能動態改變 age 變量的值,本來被賦值為 20 的,結果輸出出來卻是 10 。

這個是我在開發中無意間發現的一個功能,算是一個Trick了。但這個功能在實際的開發過程中特別有用,尤其針對于一些代碼改動后再次運行的成本比較高的場景。比如Android開發過程中,能夠在不重新打整包的情況下,動態修改頁面中各個元素的樣式、接口的請求、數據的內容等等;再比如服務端場景中,如果我們的應用支持Debug模式,則可以通過該功能實現應用無需重新部署的情況下,進行動態更改上下文邏輯的操作。

七 寫在最后

跬步至千里,小流成江海,開發工作有大小,業務需求有緩急,但終究要落到眼下,從一磚一瓦的基石開始,從一行一列的編碼開始,希望本文中能幫助到更多的研發同學。

5人點贊

日記本

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容

  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,765評論 2 59
  • 快捷鍵Alt+Enter findviewbyid強制轉換的快捷鍵 在輸入分號之前,按Alt+Enter...
    開心的小哈閱讀 2,462評論 0 2
  • IDEA使用技巧 我這里的快捷鍵方式是GNOME。 快捷鍵查詢 菜單欄HELP----->keymap refer...
    maxzhao_閱讀 7,704評論 1 10
  • 使用IDEA也有一段時間了,今天又看到了一個不錯的IDEA視頻,覺得對IDEA熟悉得更多了,在這里做下筆記,如下 ...
    sunshine9223閱讀 2,558評論 0 1
  • IDEA 超實用使用技巧分享(長篇) 前言 工欲善其事? 必先利其器 最近受部門的邀請,給入職新人統一培訓IDEA...
    Richard_易閱讀 5,011評論 1 27