最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

寫出漂亮代碼的45個(gè)小技巧

2023-02-16 17:50 作者:編程霸王花  | 我要投稿

不知道大家有沒有經(jīng)歷過維護(hù)一個(gè)已經(jīng)離職的人的代碼的痛苦,一個(gè)方法寫老長(zhǎng),還有很多的if else ,根本無法閱讀,更不知道代碼背后的含義,最重要的是沒有人可以問,此時(shí)只能心里默默地問候這個(gè)留坑的兄弟。。

其實(shí)造成這些原因的很大一部分原因是由于代碼規(guī)范的問題,如果寫的規(guī)范,注釋好,其實(shí)很多問題也就解決了。所以本文我就從代碼的編寫規(guī)范,格式的優(yōu)化,設(shè)計(jì)原則和一些常見的代碼優(yōu)化的技巧等方面總結(jié)了了45個(gè)小技巧分享給大家,如果不足,歡迎指正。


1、規(guī)范命名

命名是寫代碼中最頻繁的操作,比如類、屬性、方法、參數(shù)等。好的名字應(yīng)當(dāng)能遵循以下幾點(diǎn):

見名知意

比如需要定義一個(gè)變量需要來計(jì)數(shù)

名稱 i 沒有任何的實(shí)際意義,沒有體現(xiàn)出數(shù)量的意思,所以我們應(yīng)當(dāng)指明數(shù)量的名稱

能夠讀的出來

如下代碼:

這些變量的名稱,根本讀不出來,更別說實(shí)際意義了。

所以我們可以使用正確的可以讀出來的英文來命名

2、規(guī)范代碼格式

好的代碼格式能夠讓人感覺看起來代碼更加舒適。

好的代碼格式應(yīng)當(dāng)遵守以下幾點(diǎn):

  • 合適的空格

  • 代碼對(duì)齊,比如大括號(hào)要對(duì)齊

  • 及時(shí)換行,一行不要寫太多代碼

好在現(xiàn)在開發(fā)工具支持一鍵格式化,可以幫助美化代碼格式。


3、寫好代碼注釋

在《代碼整潔之道》這本書中作者提到了一個(gè)觀點(diǎn),注釋的恰當(dāng)用法是用來彌補(bǔ)我們?cè)谟么a表達(dá)意圖時(shí)的失敗。換句話說,當(dāng)無法通過讀代碼來了解代碼所表達(dá)的意思的時(shí)候,就需要用注釋來說明。

作者之所以這么說,是因?yàn)樽髡哂X得隨著時(shí)間的推移,代碼可能會(huì)變動(dòng),如果不及時(shí)更新注釋,那么注釋就容易產(chǎn)生誤導(dǎo),偏離代碼的實(shí)際意義。而不及時(shí)更新注釋的原因是,程序員不喜歡寫注釋。(作者很懂?。?/p>

但是這不意味著可以不寫注釋,當(dāng)通過代碼如果無法表達(dá)意思的時(shí)候,就需要注釋,比如如下代碼


為什么 id == 0 需要跳過,代碼是無法看出來了,就需要注釋了。

好的注釋應(yīng)當(dāng)滿足一下幾點(diǎn):

  • 解釋代碼的意圖,說明為什么這么寫,用來做什么

  • 對(duì)參數(shù)和返回值注釋,入?yún)⒋硎裁?,出參代表什?/p>

  • 有警示作用,比如說入?yún)⒉荒転榭?,或者代碼是不是有坑

  • 當(dāng)代碼還未完成時(shí)可以使用 todo 注釋來注釋

4、try catch 內(nèi)部代碼抽成一個(gè)方法

try catch代碼有時(shí)會(huì)干擾我們閱讀核心的代碼邏輯,這時(shí)就可以把try catch內(nèi)部主邏輯抽離成一個(gè)單獨(dú)的方法

如下圖是Eureka服務(wù)端源碼中服務(wù)下線的實(shí)現(xiàn)中的一段代碼

整個(gè)方法非常長(zhǎng),try中代碼是真正的服務(wù)下線的代碼實(shí)現(xiàn),finally可以保證讀鎖最終一定可以釋放。

所以這段代碼其實(shí)就可以對(duì)核心的邏輯進(jìn)行抽取。

5、方法別太長(zhǎng)

方法別太長(zhǎng)就是字面的意思。一旦代碼太長(zhǎng),給人的第一眼感覺就很復(fù)雜,讓人不想讀下去;同時(shí)方法太長(zhǎng)的代碼可能讀起來容易讓人摸不著頭腦,不知道哪一些代碼是同一個(gè)業(yè)務(wù)的功能。

我曾經(jīng)就遇到過一個(gè)方法寫了2000+行,各種if else判斷,我光理清代碼思路就用了很久,最終理清之后,就用策略模式給重構(gòu)了。

所以一旦方法過長(zhǎng),可以嘗試將相同業(yè)務(wù)功能的代碼單獨(dú)抽取一個(gè)方法,最后在主方法中調(diào)用即可。

6、抽取重復(fù)代碼

當(dāng)一份代碼重復(fù)出現(xiàn)在程序的多處地方,就會(huì)造成程序又臭又長(zhǎng),當(dāng)這份代碼的結(jié)構(gòu)要修改時(shí),每一處出現(xiàn)這份代碼的地方都得修改,導(dǎo)致程序的擴(kuò)展性很差。

所以一般遇到這種情況,可以抽取成一個(gè)工具類,還可以抽成一個(gè)公共的父類。

7、多用return

在有時(shí)我們平時(shí)寫代碼的情況可能會(huì)出現(xiàn)if條件套if的情況,當(dāng)if條件過多的時(shí)候可能會(huì)出現(xiàn)如下情況:

面對(duì)這種情況,可以換種思路,使用return來優(yōu)化

這樣優(yōu)化就感覺看起來更加直觀

8、if條件表達(dá)式不要太復(fù)雜

比如在如下代碼:

這段邏輯,這種條件表達(dá)式乍一看不知道是什么,仔細(xì)一看還是不知道是什么,這時(shí)就可以這么優(yōu)化

此時(shí)就很容易看懂if的邏輯了

9、優(yōu)雅地參數(shù)校驗(yàn)

當(dāng)前端傳遞給后端參數(shù)的時(shí)候,通常需要對(duì)參數(shù)進(jìn)場(chǎng)檢驗(yàn),一般可能會(huì)這么寫

這種寫雖然可以,但是當(dāng)字段的多的時(shí)候,光校驗(yàn)就占據(jù)了很長(zhǎng)的代碼,不夠優(yōu)雅。

針對(duì)參數(shù)校驗(yàn)這個(gè)問題,有第三方庫(kù)已經(jīng)封裝好了,比如hibernate-validator框架,只需要拿來用即可。

所以就在實(shí)體類上加@NotBlank、@NotNull注解來進(jìn)行校驗(yàn)

此時(shí)Controller接口就需要方法上就需要加上@Valid注解

10、統(tǒng)一返回值

后端在設(shè)計(jì)接口的時(shí)候,需要統(tǒng)一返回值

不僅是給前端參數(shù),也包括提供給第三方的接口等,這樣接口調(diào)用方法可以按照固定的格式解析代碼,不用進(jìn)行判斷。如果不一樣,相信我,前端半夜都一定會(huì)來找你。

Spring中很多方法可以做到統(tǒng)一返回值,而不用每個(gè)方法都返回,比如基于AOP,或者可以自定義HandlerMethodReturnValueHandler來實(shí)現(xiàn)統(tǒng)一返回值。

11、統(tǒng)一異常處理

當(dāng)你沒有統(tǒng)一異常處理的時(shí)候,那么所有的接口避免不了try catch操作。


每個(gè)接口都得這么玩,那不得滿屏的try catch。

所以可以基于Spring提供的統(tǒng)一異常處理機(jī)制來完成。

12、盡量不傳遞null值

這個(gè)很好理解,不傳null值可以避免方法不支持為null入?yún)r(shí)產(chǎn)生的空指針問題。

當(dāng)然為了更好的表明該方法是不是可以傳null值,可以通過@NonNull和@Nullable注解來標(biāo)記。@NonNull就表示不能傳null值,@Nullable就是可以傳null值。


13、盡量不返回null值

盡量不返回null值是為了減少調(diào)用者對(duì)返回值的為null判斷,如果無法避免返回null值,可以通過返回Optional來代替null值。

如果不想這么寫,也可以通過@NonNull和@Nullable表示方法會(huì)不會(huì)返回null值。

14、日志打印規(guī)范

好的日志打印能幫助我們快速定位問題

好的日志應(yīng)該遵循以下幾點(diǎn):

  • 可搜索性,要有明確的關(guān)鍵字信息

  • 異常日志需要打印出堆棧信息

  • 合適的日志級(jí)別,比如異常使用error,正常使用info

  • 日志內(nèi)容太大不打印,比如有時(shí)需要將圖片轉(zhuǎn)成Base64,那么這個(gè)Base64就可以不用打印

15、統(tǒng)一類庫(kù)

在一個(gè)項(xiàng)目中,可能會(huì)由于引入的依賴不同導(dǎo)致引入了很多相似功能的類庫(kù),比如常見的json類庫(kù),又或者是一些常用的工具類,當(dāng)遇到這種情況下,應(yīng)當(dāng)規(guī)范在項(xiàng)目中到底應(yīng)該使用什么類庫(kù),而不是一會(huì)用Fastjson,一會(huì)使用Gson。

16、盡量使用工具類

比如在對(duì)集合判空的時(shí)候,可以這么寫

但是一般不推薦這么寫,可以通過一些判斷的工具類來寫

不僅集合,比如字符串的判斷等等,就使用工具類,不要手動(dòng)判斷。

17、盡量不要重復(fù)造輪子

就拿格式化日期來來說,我們一般封裝成一個(gè)工具類來調(diào)用,比如如下代碼

這段代碼看似沒啥問題,但是卻忽略了SimpleDateFormat是個(gè)線程不安全的類,所以這就會(huì)引起坑。

一般對(duì)于這種已經(jīng)有開源的項(xiàng)目并且已經(jīng)做得很好的時(shí)候,比如Hutool,就可以把輪子直接拿過來用了。

18、類和方法單一職責(zé)

單一職責(zé)原則是設(shè)計(jì)模式的七大設(shè)計(jì)原則之一,它的核心意思就是字面的意思,一個(gè)類或者一個(gè)方法只做單一的功能。

就拿Nacos來說,在Nacos1.x的版本中,有這么一個(gè)接口HttpAgent

這個(gè)類只干了一件事,那就是封裝http請(qǐng)求參數(shù),向Nacos服務(wù)端發(fā)送請(qǐng)求,接收響應(yīng),這其實(shí)就是單一職責(zé)原則的體現(xiàn)。

當(dāng)其它的地方需要向Nacos服務(wù)端發(fā)送請(qǐng)求時(shí),只需要通過這個(gè)接口的實(shí)現(xiàn),傳入?yún)?shù)就可以發(fā)送請(qǐng)求了,而不需要關(guān)心如何攜帶服務(wù)端鑒權(quán)參數(shù)、http請(qǐng)求參數(shù)如何組裝等問題。


19、盡量使用聚合/組合代替繼承

繼承的弊端:

  • 靈活性低。java語言是單繼承的,無法同時(shí)繼承很多類,并且繼承容易導(dǎo)致代碼層次太深,不易于維護(hù)

  • 耦合性高。一旦父類的代碼修改,可能會(huì)影響到子類的行為

所以一般推薦使用聚合/組合代替繼承。

聚合/組合的意思就是通過成員變量的方式來使用類。

比如說,OrderService需要使用UserService,可以注入一個(gè)UserService而非通過繼承UserService。

聚合和組合的區(qū)別就是,組合是當(dāng)對(duì)象一創(chuàng)建的時(shí)候,就直接給屬性賦值,而聚合的方式可以通過set方式來設(shè)置。

組合:

聚合:

20、使用設(shè)計(jì)模式優(yōu)化代碼

在平時(shí)開發(fā)中,使用設(shè)計(jì)模式可以增加代碼的擴(kuò)展性。

比如說,當(dāng)你需要做一個(gè)可以根據(jù)不同的平臺(tái)做不同消息推送的功能時(shí),就可以使用策略模式的方式來優(yōu)化。

設(shè)計(jì)一個(gè)接口:

短信通知實(shí)現(xiàn):

app通知實(shí)現(xiàn):

最后提供一個(gè)方法,當(dāng)需要進(jìn)行消息通知時(shí),調(diào)用notifyMessage,傳入相應(yīng)的參數(shù)就行。

假設(shè)此時(shí)需要支持通過郵件通知,只需要有對(duì)應(yīng)實(shí)現(xiàn)就行。

21、不濫用設(shè)計(jì)模式

用好設(shè)計(jì)模式可以增加代碼的擴(kuò)展性,但是濫用設(shè)計(jì)模式確是不可取的。

比如上面打印Person信息的代碼,用if判斷就能夠做到效果,你說我要不用責(zé)任鏈或者什么設(shè)計(jì)模式來優(yōu)化一下吧,沒必要。

22、面向接口編程

在一些可替換的場(chǎng)景中,應(yīng)該引用父類或者抽象,而非實(shí)現(xiàn)。

舉個(gè)例子,在實(shí)際項(xiàng)目中可能需要對(duì)一些圖片進(jìn)行存儲(chǔ),但是存儲(chǔ)的方式很多,比如可以選擇阿里云的OSS,又或者是七牛云,存儲(chǔ)服務(wù)器等等。所以對(duì)于存儲(chǔ)圖片這個(gè)功能來說,這些具體的實(shí)現(xiàn)是可以相互替換的。

所以在項(xiàng)目中,我們不應(yīng)當(dāng)在代碼中耦合一個(gè)具體的實(shí)現(xiàn),而是可以提供一個(gè)存儲(chǔ)接口

如果選擇了阿里云OSS作為存儲(chǔ)服務(wù)器,那么就可以基于OSS實(shí)現(xiàn)一個(gè)FileStorage,在項(xiàng)目中哪里需要存儲(chǔ)的時(shí)候,只要實(shí)現(xiàn)注入這個(gè)接口就可以了。

假設(shè)用了一段時(shí)間之后,發(fā)現(xiàn)阿里云的OSS比較貴,此時(shí)想換成七牛云的,那么此時(shí)只需要基于七牛云的接口實(shí)現(xiàn)FileStorage接口,然后注入到IOC,那么原有代碼用到FileStorage根本不需要?jiǎng)?,?shí)現(xiàn)輕松的替換。

23、經(jīng)常重構(gòu)舊的代碼

隨著時(shí)間的推移,業(yè)務(wù)的增長(zhǎng),有的代碼可能不再適用,或者有了更好的設(shè)計(jì)方式,那么可以及時(shí)的重構(gòu)業(yè)務(wù)代碼。

就拿上面的消息通知為例,在業(yè)務(wù)剛開始的時(shí)候可能只支持短信通知,于是在代碼中就直接耦合了短信通知的代碼。但是隨著業(yè)務(wù)的增長(zhǎng),逐漸需要支持app、郵件之類的通知,那么此時(shí)就可以重構(gòu)以前的代碼,抽出一個(gè)策略接口,進(jìn)行代碼優(yōu)化。

24、null值判斷

空指針是代碼開發(fā)中的一個(gè)難題,作為程序員的基本修改,應(yīng)該要防止空指針。

可能產(chǎn)生空指針的原因:

  • 數(shù)據(jù)返回對(duì)象為null

  • 自動(dòng)拆箱導(dǎo)致空指針

  • rpc調(diào)用返回的對(duì)象可能為空格

所以在需要這些的時(shí)候,需要強(qiáng)制判斷是否為null。前面也提到可以使用Optional來優(yōu)雅地進(jìn)行null值判斷。

25、pojo類重寫toString方法

pojo一般內(nèi)部都有很多屬性,重寫toString方法可以方便在打印或者測(cè)試的時(shí)候查看內(nèi)部的屬性。

26、魔法值用常量表示

代碼里,廣東省就是一個(gè)魔法值,那么就可以將用一個(gè)常量來保存

27、資源釋放寫到finally

比如在使用一個(gè)api類鎖或者進(jìn)行IO操作的時(shí)候,需要主動(dòng)寫代碼需釋放資源,為了能夠保證資源能夠被真正釋放,那么就需要在finally中寫代碼保證資源釋放。

如圖所示,就是CopyOnWriteArrayList的add方法的實(shí)現(xiàn),最終是在finally中進(jìn)行鎖的釋放。

28、使用線程池代替手動(dòng)創(chuàng)建線程

使用線程池還有以下好處:

  • 降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。

  • 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。

  • 提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng) 的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。

所以為了達(dá)到更好的利用資源,提高響應(yīng)速度,就可以使用線程池的方式來代替手動(dòng)創(chuàng)建線程。

如果對(duì)線程池不清楚的同學(xué),可以看一下這篇文章: 7000字+24張圖帶你徹底弄懂線程池

29、線程設(shè)置名稱

在日志打印的時(shí)候,日志是可以把線程的名字給打印出來。

如上圖,日志打印出來的就是tom貓的線程。

所以,設(shè)置線程的名稱可以幫助我們更好的知道代碼是通過哪個(gè)線程執(zhí)行的,更容易排查問題。

30、涉及線程間可見性加volatile

在RocketMQ源碼中有這么一段代碼

在消費(fèi)者在從服務(wù)端拉取消息的時(shí)候,會(huì)單獨(dú)開一個(gè)線程,執(zhí)行while循環(huán),只要stopped狀態(tài)一直為false,那么就會(huì)一直循環(huán)下去,線程就一直會(huì)運(yùn)行下去,拉取消息。

當(dāng)消費(fèi)者客戶端關(guān)閉的時(shí)候,就會(huì)將stopped狀態(tài)設(shè)置為true,告訴拉取消息的線程需要停止了。但是由于并發(fā)編程中存在可見性的問題,所以雖然客戶端關(guān)閉線程將stopped狀態(tài)設(shè)置為true,但是拉取消息的線程可能看不見,不能及時(shí)感知到數(shù)據(jù)的修改,還是認(rèn)為stopped狀態(tài)設(shè)置為false,那么就還會(huì)運(yùn)行下去。

針對(duì)這種可見性的問題,java提供了一個(gè)volatile關(guān)鍵字來保證線程間的可見性。

所以,源碼中就加了volatile關(guān)鍵字。

加了volatile關(guān)鍵字之后,一旦客戶端的線程將stopped狀態(tài)設(shè)置為true時(shí)候,拉取消息的線程就能立馬知道stopped已經(jīng)是false了,那么再次執(zhí)行while條件判斷的時(shí)候,就不成立,線程就運(yùn)行結(jié)束了,然后退出。


31、考慮線程安全問題

在平時(shí)開發(fā)中,有時(shí)需要考慮并發(fā)安全的問題。

舉個(gè)例子來說,一般在調(diào)用第三方接口的時(shí)候,可能會(huì)有一個(gè)鑒權(quán)的機(jī)制,一般會(huì)攜帶一個(gè)請(qǐng)求頭token參數(shù)過去,而token也是調(diào)用第三方接口返回的,一般這種token都會(huì)有個(gè)過期時(shí)間,比如24小時(shí)。

我們一般會(huì)將token緩存到Redis中,設(shè)置一個(gè)過期時(shí)間。向第三方發(fā)送請(qǐng)求時(shí),會(huì)直接從緩存中查找,但是當(dāng)從Redis中獲取不到token的時(shí)候,我們都會(huì)重新請(qǐng)求token接口,獲取token,然后再設(shè)置到緩存中。

整個(gè)過程看起來是沒什么問題,但是實(shí)則隱藏線程安全問題。

假設(shè)當(dāng)出現(xiàn)并發(fā)的時(shí)候,同時(shí)來兩個(gè)線程AB從緩存查找,發(fā)現(xiàn)沒有,那么AB此時(shí)就會(huì)同時(shí)調(diào)用token獲取接口。假設(shè)A先獲取到token,B后獲取到token,但是由于CPU調(diào)度問題,線程B雖然后獲取到token,但是先往Redis存數(shù)據(jù),而線程A后存,覆蓋了B請(qǐng)求的token。

這下就會(huì)出現(xiàn)大問題,最新的token被覆蓋了,那么之后一定時(shí)間內(nèi)token都是無效的,接口就請(qǐng)求不通。

針對(duì)這種問題,可以使用double check機(jī)制來優(yōu)化獲取token的問題。

所以,在實(shí)際中,需要多考慮考慮業(yè)務(wù)是否有線程安全問題,有集合讀寫安全問題,那么就用線程安全的集合,業(yè)務(wù)有安全的問題,那么就可以通過加鎖的手段來解決。

32、慎用異步

雖然在使用多線程可以幫助我們提高接口的響應(yīng)速度,但是也會(huì)帶來很多問題。

事務(wù)問題

一旦使用了異步,就會(huì)導(dǎo)致兩個(gè)線程不是同一個(gè)事務(wù)的,導(dǎo)致異常之后無法正?;貪L數(shù)據(jù)。

cpu負(fù)載過高

之前有個(gè)小伙伴遇到需要同時(shí)處理幾萬調(diào)數(shù)據(jù)的需求,每條數(shù)據(jù)都需要調(diào)用很多次接口,為了達(dá)到老板期望的時(shí)間要求,使用了多線程跑,開了很多線程,此時(shí)會(huì)發(fā)現(xiàn)系統(tǒng)的cpu會(huì)飆升

意想不到的異常

還是上面的提到的例子,在測(cè)試的時(shí)候就發(fā)現(xiàn),由于并發(fā)量激增,在請(qǐng)求第三方接口的時(shí)候,返回了很多錯(cuò)誤信息,導(dǎo)致有的數(shù)據(jù)沒有處理成功。

雖然說慎用異步,但不代表不用,如果可以保證事務(wù)的問題,或是CPU負(fù)載不會(huì)高的話,那么還是可以使用的。


33、減小鎖的范圍

減小鎖的范圍就是給需要加鎖的代碼加鎖,不需要加鎖的代碼不要加鎖。這樣就能減少加鎖的時(shí)間,從而可以較少鎖互斥的時(shí)間,提高效率。

比如CopyOnWriteArrayList的addAll方法的實(shí)現(xiàn),lock.lock(); 代碼完全可以放到代碼的第一行,但是作者并沒有,因?yàn)榍懊媾袛嗟拇a不會(huì)有線程安全的問題,不放到加鎖代碼中可以減少鎖搶占和占有的時(shí)間。

34、有類型區(qū)分時(shí)定義好枚舉

比如在項(xiàng)目中不同的類型的業(yè)務(wù)可能需要上傳各種各樣的附件,此時(shí)就可以定義好不同的一個(gè)附件的枚舉,來區(qū)分不同業(yè)務(wù)的附件。

不要在代碼中直接寫死,不定義枚舉,代碼閱讀起來非常困難,直接看到數(shù)字都是懵逼的。。

35、遠(yuǎn)程接口調(diào)用設(shè)置超時(shí)時(shí)間

比如在進(jìn)行微服務(wù)之間進(jìn)行rpc調(diào)用的時(shí)候,又或者在調(diào)用第三方提供的接口的時(shí)候,需要設(shè)置超時(shí)時(shí)間,防止因?yàn)楦鞣N原因,導(dǎo)致線程”卡死“在那。

我以前就遇到過線上就遇到過這種問題。當(dāng)時(shí)的業(yè)務(wù)是訂閱kafka的消息,然后向第三方上傳數(shù)據(jù)。在某個(gè)周末,突然就接到電話,說數(shù)據(jù)無法上傳了,通過排查線上的服務(wù)器才發(fā)現(xiàn)所有的線程都線程”卡死“了,最后定位到代碼才發(fā)現(xiàn)原來是沒有設(shè)置超時(shí)時(shí)間。

36、集合使用應(yīng)當(dāng)指明初始化大小

比如在寫代碼的時(shí)候,經(jīng)常會(huì)用到List、Map來臨時(shí)存儲(chǔ)數(shù)據(jù),其中最常用的就是ArrayList和HashMap。但是用不好可能也會(huì)導(dǎo)致性能的問題。

比如說,在ArrayList中,底層是基于數(shù)組來存儲(chǔ)的,數(shù)組是一旦確定大小是無法再改變?nèi)萘康?。但不斷的往ArrayList中存儲(chǔ)數(shù)據(jù)的時(shí)候,總有那么一刻會(huì)導(dǎo)致數(shù)組的容量滿了,無法再存儲(chǔ)其它元素,此時(shí)就需要對(duì)數(shù)組擴(kuò)容。所謂的擴(kuò)容就是新創(chuàng)建一個(gè)容量是原來1.5倍的數(shù)組,將原有的數(shù)據(jù)給拷貝到新的數(shù)組上,然后用新的數(shù)組替代原來的數(shù)組。

在擴(kuò)容的過程中,由于涉及到數(shù)組的拷貝,就會(huì)導(dǎo)致性能消耗;同時(shí)HashMap也會(huì)由于擴(kuò)容的問題,消耗性能。所以在使用這類集合時(shí)可以在構(gòu)造的時(shí)候指定集合的容量大小。

37、盡量不要使用BeanUtils來拷貝屬性

在開發(fā)中經(jīng)常需要對(duì)JavaBean進(jìn)行轉(zhuǎn)換,但是又不想一個(gè)一個(gè)手動(dòng)set,比較麻煩,所以一般會(huì)使用屬性拷貝的一些工具,比如說Spring提供的BeanUtils來拷貝。不得不說,使用BeanUtils來拷貝屬性是真的舒服,使用一行代碼可以代替幾行甚至十幾行代碼,我也喜歡用。

但是喜歡歸喜歡,但是會(huì)帶來性能問題,因?yàn)榈讓邮峭ㄟ^反射來的拷貝屬性的,所以盡量不要用BeanUtils來拷貝屬性。

比如你可以裝個(gè)JavaBean轉(zhuǎn)換的插件,幫你自動(dòng)生成轉(zhuǎn)換代碼;又或者可以使用性能更高的MapStruct來進(jìn)行JavaBean轉(zhuǎn)換,MapStruct底層是通過調(diào)用(settter/getter)來實(shí)現(xiàn)的,而不是反射來快速執(zhí)行。

38、使用StringBuilder進(jìn)行字符串拼接

如下代碼:


使用 + 拼接字符串的時(shí)候,會(huì)創(chuàng)建一個(gè)StringBuilder,然后將要拼接的字符串追加到StringBuilder,再toString,這樣如果多次拼接就會(huì)執(zhí)行很多次的創(chuàng)建StringBuilder,z執(zhí)行toString的操作。

所以可以手動(dòng)通過StringBuilder拼接,這樣只會(huì)創(chuàng)建一次StringBuilder,效率更高。

39、@Transactional應(yīng)指定回滾的異常類型

平時(shí)在寫代碼的時(shí)候需要通過rollbackFor顯示指定需要對(duì)什么異?;貪L,原因在這:

默認(rèn)是只能回滾RuntimeException和Error異常,所以需要手動(dòng)指定,比如指定成Expection等。

40、謹(jǐn)慎方法內(nèi)部調(diào)用動(dòng)態(tài)代理的方法

如下事務(wù)代碼

update調(diào)用了加了@Transactional注解的updatePerson方法,那么此時(shí)updatePerson的事務(wù)就是失效。

其實(shí)失效的原因不是事務(wù)的鍋,是由AOP機(jī)制決定的,因?yàn)槭聞?wù)是基于AOP實(shí)現(xiàn)的。AOP是基于對(duì)象的代理,當(dāng)內(nèi)部方法調(diào)用時(shí),走的不是動(dòng)態(tài)代理對(duì)象的方法,而是原有對(duì)象的方法調(diào)用,如此就走不到動(dòng)態(tài)代理的代碼,就會(huì)失效了。

如果實(shí)在需要讓動(dòng)態(tài)代理生效,可以注入自己的代理對(duì)象


41、需要什么字段select什么字段

查詢?nèi)侄斡幸韵聨c(diǎn)壞處:

增加不必要的字段的網(wǎng)絡(luò)傳輸

比如有些文本的字段,存儲(chǔ)的數(shù)據(jù)非常長(zhǎng),但是本次業(yè)務(wù)使用不到,但是如果查了就會(huì)把這個(gè)數(shù)據(jù)返回給客戶端,增加了網(wǎng)絡(luò)傳輸?shù)呢?fù)擔(dān)

會(huì)導(dǎo)致無法使用到覆蓋索引

比如說,現(xiàn)在有身份證號(hào)和姓名做了聯(lián)合索引,現(xiàn)在只需要根據(jù)身份證號(hào)查詢姓名,如果直接select name 的話,那么在遍歷索引的時(shí)候,發(fā)現(xiàn)要查詢的字段在索引中已經(jīng)存在,那么此時(shí)就會(huì)直接從索引中將name字段的數(shù)據(jù)查出來,返回,而不會(huì)繼續(xù)去查找聚簇索引,減少回表的操作。

所以建議是需要使用什么字段查詢什么字段。比如mp也支持在構(gòu)建查詢條件的時(shí)候,查詢某個(gè)具體的字段。

42、不循環(huán)調(diào)用數(shù)據(jù)庫(kù)

不要在循環(huán)中訪問數(shù)據(jù)庫(kù),這樣會(huì)嚴(yán)重影響數(shù)據(jù)庫(kù)性能。

比如需要查詢一批人員的信息,人員的信息存在基本信息表和擴(kuò)展表中,錯(cuò)誤的代碼如下:

遍歷每個(gè)人員的基本信息,去數(shù)據(jù)庫(kù)查找。

正確的方法應(yīng)該先批量查出來,然后轉(zhuǎn)成map:

43、用業(yè)務(wù)代碼代替多表join

如上面代碼所示,原本也可以將兩張表根據(jù)人員的id進(jìn)行關(guān)聯(lián)查詢。但是不推薦這么,阿里也禁止多表join的操作

而之所以會(huì)禁用,是因?yàn)閖oin的效率比較低。

MySQL是使用了嵌套循環(huán)的方式來實(shí)現(xiàn)關(guān)聯(lián)查詢的,也就是for循環(huán)會(huì)套for循環(huán)的意思。用第一張表做外循環(huán),第二張表做內(nèi)循環(huán),外循環(huán)的每一條記錄跟內(nèi)循環(huán)中的記錄作比較,符合條件的就輸出,這種效率肯定低。

44、裝上阿里代碼檢查插件

我們平時(shí)寫代碼由于各種因?yàn)?,比如什么領(lǐng)導(dǎo)啊,項(xiàng)目經(jīng)理啊,會(huì)一直催進(jìn)度,導(dǎo)致寫代碼都來不及思考,怎么快怎么來,cv大法上線,雖然有心想寫好代碼,但是手確不聽使喚。所以我建議裝一個(gè)阿里的代碼規(guī)范插件,如果有代碼不規(guī)范,會(huì)有提醒,這樣就可以知道哪些是可以優(yōu)化的了。


如果你有強(qiáng)迫癥,相信我,裝了這款插件,你的代碼會(huì)寫的很漂亮。

45、及時(shí)跟同事溝通

寫代碼的時(shí)候不能閉門造車,及時(shí)跟同事溝通,比如剛進(jìn)入一個(gè)新的項(xiàng)目的,對(duì)項(xiàng)目工程不熟悉,一些技術(shù)方案不了解,如果上來就直接寫代碼,很有可能就會(huì)踩坑。


寫出漂亮代碼的45個(gè)小技巧的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
黄浦区| 日照市| 长沙市| 安化县| 乐昌市| 东丽区| 呼伦贝尔市| 沭阳县| 东源县| 田东县| 杭州市| 铜陵市| 乐东| 崇礼县| 文山县| 卓资县| 淮滨县| 鹿邑县| 巴东县| 安徽省| 泽普县| 邹平县| 曲麻莱县| 贵定县| 乌审旗| 淳安县| 宜城市| 新龙县| 成武县| 亚东县| 宜黄县| 治县。| 大方县| 平和县| 麻城市| 石嘴山市| 驻马店市| 毕节市| 富宁县| 普兰县| 凤冈县|