大家好,我是三友~~ 不知道大家有沒有經(jīng)歷過維護一個已經(jīng)離職的人的代碼的痛苦,一個方法寫老長,還有很多的if else ,根本無法閱讀,更不知道代碼背后的含義,最重要的是沒有人可以問,此時只能心里默默地問候這個留坑的兄弟。。 其實造成這些原因的很大一部分原因是由于代碼規(guī)范的問題,如果寫的規(guī)范,注釋好,其實很多問題也就解決了。所以本文我就從代碼的編寫規(guī)范,格式的優(yōu)化,設(shè)計原則和一些常見的代碼優(yōu)化的技巧等方面總結(jié)了了45個小技巧分享給大家,如果不足,歡迎指正。 ![]() 1、規(guī)范命名命名是寫代碼中最頻繁的操作,比如類、屬性、方法、參數(shù)等。好的名字應(yīng)當能遵循以下幾點: 見名知意比如需要定義一個變量需要來計數(shù) int i = 0; 名稱 i 沒有任何的實際意義,沒有體現(xiàn)出數(shù)量的意思,所以我們應(yīng)當指明數(shù)量的名稱
能夠讀的出來如下代碼: private String sfzh; 這些變量的名稱,根本讀不出來,更別說實際意義了。 所以我們可以使用正確的可以讀出來的英文來命名
2、規(guī)范代碼格式好的代碼格式能夠讓人感覺看起來代碼更加舒適。 好的代碼格式應(yīng)當遵守以下幾點:
好在現(xiàn)在開發(fā)工具支持一鍵格式化,可以幫助美化代碼格式。 3、寫好代碼注釋在《代碼整潔之道》這本書中作者提到了一個觀點,注釋的恰當用法是用來彌補我們在用代碼表達意圖時的失敗。換句話說,當無法通過讀代碼來了解代碼所表達的意思的時候,就需要用注釋來說明。 作者之所以這么說,是因為作者覺得隨著時間的推移,代碼可能會變動,如果不及時更新注釋,那么注釋就容易產(chǎn)生誤導(dǎo),偏離代碼的實際意義。而不及時更新注釋的原因是,程序員不喜歡寫注釋。(作者很懂?。?/p> 但是這不意味著可以不寫注釋,當通過代碼如果無法表達意思的時候,就需要注釋,比如如下代碼 for (Integer id : ids) { 為什么 id == 0 需要跳過,代碼是無法看出來了,就需要注釋了。 好的注釋應(yīng)當滿足一下幾點:
4、try catch 內(nèi)部代碼抽成一個方法try catch代碼有時會干擾我們閱讀核心的代碼邏輯,這時就可以把try catch內(nèi)部主邏輯抽離成一個單獨的方法 如下圖是Eureka服務(wù)端源碼中服務(wù)下線的實現(xiàn)中的一段代碼 ![]() 整個方法非常長,try中代碼是真正的服務(wù)下線的代碼實現(xiàn),finally可以保證讀鎖最終一定可以釋放。 所以這段代碼其實就可以對核心的邏輯進行抽取。
5、方法別太長方法別太長就是字面的意思。一旦代碼太長,給人的第一眼感覺就很復(fù)雜,讓人不想讀下去;同時方法太長的代碼可能讀起來容易讓人摸不著頭腦,不知道哪一些代碼是同一個業(yè)務(wù)的功能。 我曾經(jīng)就遇到過一個方法寫了2000+行,各種if else判斷,我光理清代碼思路就用了很久,最終理清之后,就用策略模式給重構(gòu)了。 所以一旦方法過長,可以嘗試將相同業(yè)務(wù)功能的代碼單獨抽取一個方法,最后在主方法中調(diào)用即可。 6、抽取重復(fù)代碼當一份代碼重復(fù)出現(xiàn)在程序的多處地方,就會造成程序又臭又長,當這份代碼的結(jié)構(gòu)要修改時,每一處出現(xiàn)這份代碼的地方都得修改,導(dǎo)致程序的擴展性很差。 所以一般遇到這種情況,可以抽取成一個工具類,還可以抽成一個公共的父類。 7、多用return在有時我們平時寫代碼的情況可能會出現(xiàn)if條件套if的情況,當if條件過多的時候可能會出現(xiàn)如下情況: if (條件1) { 面對這種情況,可以換種思路,使用return來優(yōu)化
這樣優(yōu)化就感覺看起來更加直觀 8、if條件表達式不要太復(fù)雜比如在如下代碼: if (((StringUtils.isBlank(person.getName()) 這段邏輯,這種條件表達式乍一看不知道是什么,仔細一看還是不知道是什么,這時就可以這么優(yōu)化
此時就很容易看懂if的邏輯了 9、優(yōu)雅地參數(shù)校驗當前端傳遞給后端參數(shù)的時候,通常需要對參數(shù)進場檢驗,一般可能會這么寫 @PostMapping 這種寫雖然可以,但是當字段的多的時候,光校驗就占據(jù)了很長的代碼,不夠優(yōu)雅。 針對參數(shù)校驗這個問題,有第三方庫已經(jīng)封裝好了,比如hibernate-validator框架,只需要拿來用即可。 所以就在實體類上加@NotBlank、@NotNull注解來進行校驗
此時Controller接口就需要方法上就需要加上@Valid注解 @PostMapping 10、統(tǒng)一返回值后端在設(shè)計接口的時候,需要統(tǒng)一返回值
不僅是給前端參數(shù),也包括提供給第三方的接口等,這樣接口調(diào)用方法可以按照固定的格式解析代碼,不用進行判斷。如果不一樣,相信我,前端半夜都一定會來找你。 Spring中很多方法可以做到統(tǒng)一返回值,而不用每個方法都返回,比如基于AOP,或者可以自定義HandlerMethodReturnValueHandler來實現(xiàn)統(tǒng)一返回值。 11、統(tǒng)一異常處理當你沒有統(tǒng)一異常處理的時候,那么所有的接口避免不了try catch操作。 @GetMapping('/{id}') 每個接口都得這么玩,那不得滿屏的try catch。 所以可以基于Spring提供的統(tǒng)一異常處理機制來完成。 12、盡量不傳遞null值這個很好理解,不傳null值可以避免方法不支持為null入?yún)r產(chǎn)生的空指針問題。 當然為了更好的表明該方法是不是可以傳null值,可以通過@NonNull和@Nullable注解來標記。@NonNull就表示不能傳null值,@Nullable就是可以傳null值。
13、盡量不返回null值盡量不返回null值是為了減少調(diào)用者對返回值的為null判斷,如果無法避免返回null值,可以通過返回Optional來代替null值。 public Optional<Person> getPersonById(Long personId) { 如果不想這么寫,也可以通過@NonNull和@Nullable表示方法會不會返回null值。 14、日志打印規(guī)范好的日志打印能幫助我們快速定位問題 好的日志應(yīng)該遵循以下幾點:
15、統(tǒng)一類庫在一個項目中,可能會由于引入的依賴不同導(dǎo)致引入了很多相似功能的類庫,比如常見的json類庫,又或者是一些常用的工具類,當遇到這種情況下,應(yīng)當規(guī)范在項目中到底應(yīng)該使用什么類庫,而不是一會用Fastjson,一會使用Gson。 16、盡量使用工具類比如在對集合判空的時候,可以這么寫
但是一般不推薦這么寫,可以通過一些判斷的工具類來寫 public void updatePersons(List<Person> persons) { 不僅集合,比如字符串的判斷等等,就使用工具類,不要手動判斷。 17、盡量不要重復(fù)造輪子就拿格式化日期來來說,我們一般封裝成一個工具類來調(diào)用,比如如下代碼
這段代碼看似沒啥問題,但是卻忽略了SimpleDateFormat是個線程不安全的類,所以這就會引起坑。 一般對于這種已經(jīng)有開源的項目并且已經(jīng)做得很好的時候,比如Hutool,就可以把輪子直接拿過來用了。 18、類和方法單一職責單一職責原則是設(shè)計模式的七大設(shè)計原則之一,它的核心意思就是字面的意思,一個類或者一個方法只做單一的功能。 就拿Nacos來說,在Nacos1.x的版本中,有這么一個接口HttpAgent ![]() 這個類只干了一件事,那就是封裝http請求參數(shù),向Nacos服務(wù)端發(fā)送請求,接收響應(yīng),這其實就是單一職責原則的體現(xiàn)。 當其它的地方需要向Nacos服務(wù)端發(fā)送請求時,只需要通過這個接口的實現(xiàn),傳入?yún)?shù)就可以發(fā)送請求了,而不需要關(guān)心如何攜帶服務(wù)端鑒權(quán)參數(shù)、http請求參數(shù)如何組裝等問題。 19、盡量使用聚合/組合代替繼承繼承的弊端:
所以一般推薦使用聚合/組合代替繼承。 聚合/組合的意思就是通過成員變量的方式來使用類。 比如說,OrderService需要使用UserService,可以注入一個UserService而非通過繼承UserService。 聚合和組合的區(qū)別就是,組合是當對象一創(chuàng)建的時候,就直接給屬性賦值,而聚合的方式可以通過set方式來設(shè)置。 組合: public class OrderService { 聚合:
20、使用設(shè)計模式優(yōu)化代碼在平時開發(fā)中,使用設(shè)計模式可以增加代碼的擴展性。 比如說,當你需要做一個可以根據(jù)不同的平臺做不同消息推送的功能時,就可以使用策略模式的方式來優(yōu)化。 設(shè)計一個接口: public interface MessageNotifier { 短信通知實現(xiàn):
app通知實現(xiàn): public class AppMessageNotifier implements MessageNotifier { 最后提供一個方法,當需要進行消息通知時,調(diào)用notifyMessage,傳入相應(yīng)的參數(shù)就行。
假設(shè)此時需要支持通過郵件通知,只需要有對應(yīng)實現(xiàn)就行。 21、不濫用設(shè)計模式用好設(shè)計模式可以增加代碼的擴展性,但是濫用設(shè)計模式確是不可取的。 public void printPerson(Person person) { 比如上面打印Person信息的代碼,用if判斷就能夠做到效果,你說我要不用責任鏈或者什么設(shè)計模式來優(yōu)化一下吧,沒必要。 22、面向接口編程在一些可替換的場景中,應(yīng)該引用父類或者抽象,而非實現(xiàn)。 舉個例子,在實際項目中可能需要對一些圖片進行存儲,但是存儲的方式很多,比如可以選擇阿里云的OSS,又或者是七牛云,存儲服務(wù)器等等。所以對于存儲圖片這個功能來說,這些具體的實現(xiàn)是可以相互替換的。 所以在項目中,我們不應(yīng)當在代碼中耦合一個具體的實現(xiàn),而是可以提供一個存儲接口
如果選擇了阿里云OSS作為存儲服務(wù)器,那么就可以基于OSS實現(xiàn)一個FileStorage,在項目中哪里需要存儲的時候,只要實現(xiàn)注入這個接口就可以了。 @Autowired 假設(shè)用了一段時間之后,發(fā)現(xiàn)阿里云的OSS比較貴,此時想換成七牛云的,那么此時只需要基于七牛云的接口實現(xiàn)FileStorage接口,然后注入到IOC,那么原有代碼用到FileStorage根本不需要動,實現(xiàn)輕松的替換。 23、經(jīng)常重構(gòu)舊的代碼隨著時間的推移,業(yè)務(wù)的增長,有的代碼可能不再適用,或者有了更好的設(shè)計方式,那么可以及時的重構(gòu)業(yè)務(wù)代碼。 就拿上面的消息通知為例,在業(yè)務(wù)剛開始的時候可能只支持短信通知,于是在代碼中就直接耦合了短信通知的代碼。但是隨著業(yè)務(wù)的增長,逐漸需要支持app、郵件之類的通知,那么此時就可以重構(gòu)以前的代碼,抽出一個策略接口,進行代碼優(yōu)化。 24、null值判斷空指針是代碼開發(fā)中的一個難題,作為程序員的基本修改,應(yīng)該要防止空指針。 可能產(chǎn)生空指針的原因:
所以在需要這些的時候,需要強制判斷是否為null。前面也提到可以使用Optional來優(yōu)雅地進行null值判斷。 25、pojo類重寫toString方法pojo一般內(nèi)部都有很多屬性,重寫toString方法可以方便在打印或者測試的時候查看內(nèi)部的屬性。 26、魔法值用常量表示
代碼里,廣東省就是一個魔法值,那么就可以將用一個常量來保存 private static final String GUANG_DONG_PROVINCE = '廣東省'; 27、資源釋放寫到finally比如在使用一個api類鎖或者進行IO操作的時候,需要主動寫代碼需釋放資源,為了能夠保證資源能夠被真正釋放,那么就需要在finally中寫代碼保證資源釋放。 ![]() 如圖所示,就是CopyOnWriteArrayList的add方法的實現(xiàn),最終是在finally中進行鎖的釋放。 28、使用線程池代替手動創(chuàng)建線程使用線程池還有以下好處:
所以為了達到更好的利用資源,提高響應(yīng)速度,就可以使用線程池的方式來代替手動創(chuàng)建線程。 如果對線程池不清楚的同學(xué),可以看一下這篇文章:7000字+24張圖帶你徹底弄懂線程池 29、線程設(shè)置名稱在日志打印的時候,日志是可以把線程的名字給打印出來。 ![]() 如上圖,日志打印出來的就是tom貓的線程。 所以,設(shè)置線程的名稱可以幫助我們更好的知道代碼是通過哪個線程執(zhí)行的,更容易排查問題。 30、涉及線程間可見性加volatile在RocketMQ源碼中有這么一段代碼 ![]() 在消費者在從服務(wù)端拉取消息的時候,會單獨開一個線程,執(zhí)行while循環(huán),只要stopped狀態(tài)一直為false,那么就會一直循環(huán)下去,線程就一直會運行下去,拉取消息。 當消費者客戶端關(guān)閉的時候,就會將stopped狀態(tài)設(shè)置為true,告訴拉取消息的線程需要停止了。但是由于并發(fā)編程中存在可見性的問題,所以雖然客戶端關(guān)閉線程將stopped狀態(tài)設(shè)置為true,但是拉取消息的線程可能看不見,不能及時感知到數(shù)據(jù)的修改,還是認為stopped狀態(tài)設(shè)置為false,那么就還會運行下去。 針對這種可見性的問題,java提供了一個volatile關(guān)鍵字來保證線程間的可見性。 ![]() 所以,源碼中就加了volatile關(guān)鍵字。 加了volatile關(guān)鍵字之后,一旦客戶端的線程將stopped狀態(tài)設(shè)置為true時候,拉取消息的線程就能立馬知道stopped已經(jīng)是false了,那么再次執(zhí)行while條件判斷的時候,就不成立,線程就運行結(jié)束了,然后退出。 31、考慮線程安全問題在平時開發(fā)中,有時需要考慮并發(fā)安全的問題。 舉個例子來說,一般在調(diào)用第三方接口的時候,可能會有一個鑒權(quán)的機制,一般會攜帶一個請求頭token參數(shù)過去,而token也是調(diào)用第三方接口返回的,一般這種token都會有個過期時間,比如24小時。 我們一般會將token緩存到Redis中,設(shè)置一個過期時間。向第三方發(fā)送請求時,會直接從緩存中查找,但是當從Redis中獲取不到token的時候,我們都會重新請求token接口,獲取token,然后再設(shè)置到緩存中。 整個過程看起來是沒什么問題,但是實則隱藏線程安全問題。 假設(shè)當出現(xiàn)并發(fā)的時候,同時來兩個線程AB從緩存查找,發(fā)現(xiàn)沒有,那么AB此時就會同時調(diào)用token獲取接口。假設(shè)A先獲取到token,B后獲取到token,但是由于CPU調(diào)度問題,線程B雖然后獲取到token,但是先往Redis存數(shù)據(jù),而線程A后存,覆蓋了B請求的token。 這下就會出現(xiàn)大問題,最新的token被覆蓋了,那么之后一定時間內(nèi)token都是無效的,接口就請求不通。 針對這種問題,可以使用double check機制來優(yōu)化獲取token的問題。 所以,在實際中,需要多考慮考慮業(yè)務(wù)是否有線程安全問題,有集合讀寫安全問題,那么就用線程安全的集合,業(yè)務(wù)有安全的問題,那么就可以通過加鎖的手段來解決。 32、慎用異步雖然在使用多線程可以幫助我們提高接口的響應(yīng)速度,但是也會帶來很多問題。 事務(wù)問題一旦使用了異步,就會導(dǎo)致兩個線程不是同一個事務(wù)的,導(dǎo)致異常之后無法正?;貪L數(shù)據(jù)。 cpu負載過高之前有個小伙伴遇到需要同時處理幾萬調(diào)數(shù)據(jù)的需求,每條數(shù)據(jù)都需要調(diào)用很多次接口,為了達到老板期望的時間要求,使用了多線程跑,開了很多線程,此時會發(fā)現(xiàn)系統(tǒng)的cpu會飆升 意想不到的異常還是上面的提到的例子,在測試的時候就發(fā)現(xiàn),由于并發(fā)量激增,在請求第三方接口的時候,返回了很多錯誤信息,導(dǎo)致有的數(shù)據(jù)沒有處理成功。 雖然說慎用異步,但不代表不用,如果可以保證事務(wù)的問題,或是CPU負載不會高的話,那么還是可以使用的。 33、減小鎖的范圍減小鎖的范圍就是給需要加鎖的代碼加鎖,不需要加鎖的代碼不要加鎖。這樣就能減少加鎖的時間,從而可以較少鎖互斥的時間,提高效率。 ![]() 比如CopyOnWriteArrayList的addAll方法的實現(xiàn),lock.lock(); 代碼完全可以放到代碼的第一行,但是作者并沒有,因為前面判斷的代碼不會有線程安全的問題,不放到加鎖代碼中可以減少鎖搶占和占有的時間。 34、有類型區(qū)分時定義好枚舉比如在項目中不同的類型的業(yè)務(wù)可能需要上傳各種各樣的附件,此時就可以定義好不同的一個附件的枚舉,來區(qū)分不同業(yè)務(wù)的附件。 不要在代碼中直接寫死,不定義枚舉,代碼閱讀起來非常困難,直接看到數(shù)字都是懵逼的。。 35、遠程接口調(diào)用設(shè)置超時時間比如在進行微服務(wù)之間進行rpc調(diào)用的時候,又或者在調(diào)用第三方提供的接口的時候,需要設(shè)置超時時間,防止因為各種原因,導(dǎo)致線程”卡死“在那。 我以前就遇到過線上就遇到過這種問題。當時的業(yè)務(wù)是訂閱kafka的消息,然后向第三方上傳數(shù)據(jù)。在某個周末,突然就接到電話,說數(shù)據(jù)無法上傳了,通過排查線上的服務(wù)器才發(fā)現(xiàn)所有的線程都線程”卡死“了,最后定位到代碼才發(fā)現(xiàn)原來是沒有設(shè)置超時時間。 36、集合使用應(yīng)當指明初始化大小比如在寫代碼的時候,經(jīng)常會用到List、Map來臨時存儲數(shù)據(jù),其中最常用的就是ArrayList和HashMap。但是用不好可能也會導(dǎo)致性能的問題。 比如說,在ArrayList中,底層是基于數(shù)組來存儲的,數(shù)組是一旦確定大小是無法再改變?nèi)萘康?。但不斷的往ArrayList中存儲數(shù)據(jù)的時候,總有那么一刻會導(dǎo)致數(shù)組的容量滿了,無法再存儲其它元素,此時就需要對數(shù)組擴容。所謂的擴容就是新創(chuàng)建一個容量是原來1.5倍的數(shù)組,將原有的數(shù)據(jù)給拷貝到新的數(shù)組上,然后用新的數(shù)組替代原來的數(shù)組。 在擴容的過程中,由于涉及到數(shù)組的拷貝,就會導(dǎo)致性能消耗;同時HashMap也會由于擴容的問題,消耗性能。所以在使用這類集合時可以在構(gòu)造的時候指定集合的容量大小。 37、盡量不要使用BeanUtils來拷貝屬性在開發(fā)中經(jīng)常需要對JavaBean進行轉(zhuǎn)換,但是又不想一個一個手動set,比較麻煩,所以一般會使用屬性拷貝的一些工具,比如說Spring提供的BeanUtils來拷貝。不得不說,使用BeanUtils來拷貝屬性是真的舒服,使用一行代碼可以代替幾行甚至十幾行代碼,我也喜歡用。 但是喜歡歸喜歡,但是會帶來性能問題,因為底層是通過反射來的拷貝屬性的,所以盡量不要用BeanUtils來拷貝屬性。 比如你可以裝個JavaBean轉(zhuǎn)換的插件,幫你自動生成轉(zhuǎn)換代碼;又或者可以使用性能更高的MapStruct來進行JavaBean轉(zhuǎn)換,MapStruct底層是通過調(diào)用(settter/getter)來實現(xiàn)的,而不是反射來快速執(zhí)行。 38、使用StringBuilder進行字符串拼接如下代碼:
使用 + 拼接字符串的時候,會創(chuàng)建一個StringBuilder,然后將要拼接的字符串追加到StringBuilder,再toString,這樣如果多次拼接就會執(zhí)行很多次的創(chuàng)建StringBuilder,z執(zhí)行toString的操作。 所以可以手動通過StringBuilder拼接,這樣只會創(chuàng)建一次StringBuilder,效率更高。 StringBuilder sb = new StringBuilder(); 39、@Transactional應(yīng)指定回滾的異常類型平時在寫代碼的時候需要通過rollbackFor顯示指定需要對什么異常回滾,原因在這: ![]() 默認是只能回滾RuntimeException和Error異常,所以需要手動指定,比如指定成Expection等。 40、謹慎方法內(nèi)部調(diào)用動態(tài)代理的方法如下事務(wù)代碼
update調(diào)用了加了@Transactional注解的updatePerson方法,那么此時updatePerson的事務(wù)就是失效。 其實失效的原因不是事務(wù)的鍋,是由AOP機制決定的,因為事務(wù)是基于AOP實現(xiàn)的。AOP是基于對象的代理,當內(nèi)部方法調(diào)用時,走的不是動態(tài)代理對象的方法,而是原有對象的方法調(diào)用,如此就走不到動態(tài)代理的代碼,就會失效了。 如果實在需要讓動態(tài)代理生效,可以注入自己的代理對象 @Service 41、需要什么字段select什么字段查詢?nèi)侄斡幸韵聨c壞處: 增加不必要的字段的網(wǎng)絡(luò)傳輸比如有些文本的字段,存儲的數(shù)據(jù)非常長,但是本次業(yè)務(wù)使用不到,但是如果查了就會把這個數(shù)據(jù)返回給客戶端,增加了網(wǎng)絡(luò)傳輸?shù)呢摀?/p> 會導(dǎo)致無法使用到覆蓋索引比如說,現(xiàn)在有身份證號和姓名做了聯(lián)合索引,現(xiàn)在只需要根據(jù)身份證號查詢姓名,如果直接select name 的話,那么在遍歷索引的時候,發(fā)現(xiàn)要查詢的字段在索引中已經(jīng)存在,那么此時就會直接從索引中將name字段的數(shù)據(jù)查出來,返回,而不會繼續(xù)去查找聚簇索引,減少回表的操作。 所以建議是需要使用什么字段查詢什么字段。比如mp也支持在構(gòu)建查詢條件的時候,查詢某個具體的字段。
42、不循環(huán)調(diào)用數(shù)據(jù)庫不要在循環(huán)中訪問數(shù)據(jù)庫,這樣會嚴重影響數(shù)據(jù)庫性能。 比如需要查詢一批人員的信息,人員的信息存在基本信息表和擴展表中,錯誤的代碼如下: public List<PersonVO> selectPersons(List<Long> personIds) { 遍歷每個人員的基本信息,去數(shù)據(jù)庫查找。 正確的方法應(yīng)該先批量查出來,然后轉(zhuǎn)成map:
43、用業(yè)務(wù)代碼代替多表join如上面代碼所示,原本也可以將兩張表根據(jù)人員的id進行關(guān)聯(lián)查詢。但是不推薦這么,阿里也禁止多表join的操作 ![]() 而之所以會禁用,是因為join的效率比較低。 MySQL是使用了嵌套循環(huán)的方式來實現(xiàn)關(guān)聯(lián)查詢的,也就是for循環(huán)會套for循環(huán)的意思。用第一張表做外循環(huán),第二張表做內(nèi)循環(huán),外循環(huán)的每一條記錄跟內(nèi)循環(huán)中的記錄作比較,符合條件的就輸出,這種效率肯定低。 44、裝上阿里代碼檢查插件我們平時寫代碼由于各種因為,比如什么領(lǐng)導(dǎo)啊,項目經(jīng)理啊,會一直催進度,導(dǎo)致寫代碼都來不及思考,怎么快怎么來,cv大法上線,雖然有心想寫好代碼,但是手確不聽使喚。所以我建議裝一個阿里的代碼規(guī)范插件,如果有代碼不規(guī)范,會有提醒,這樣就可以知道哪些是可以優(yōu)化的了。 ![]() 如果你有強迫癥,相信我,裝了這款插件,你的代碼會寫的很漂亮。 45、及時跟同事溝通寫代碼的時候不能閉門造車,及時跟同事溝通,比如剛進入一個新的項目的,對項目工程不熟悉,一些技術(shù)方案不了解,如果上來就直接寫代碼,很有可能就會踩坑。 參考資料:《代碼整潔之道》 《阿里巴巴Java開發(fā)手冊》 |
|