久久综合九色综合97婷婷-美女视频黄频a免费-精品日本一区二区三区在线观看-日韩中文无码有码免费视频-亚洲中文字幕无码专区-扒开双腿疯狂进出爽爽爽动态照片-国产乱理伦片在线观看夜-高清极品美女毛茸茸-欧美寡妇性猛交XXX-国产亚洲精品99在线播放-日韩美女毛片又爽又大毛片,99久久久无码国产精品9,国产成a人片在线观看视频下载,欧美疯狂xxxx吞精视频

有趣生活

當前位置:首頁>時尚>100個寫代碼技巧(寫出漂亮代碼的45個小技巧)

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

發布時間:2024-08-04閱讀(13)

導讀不知道大家有沒有經歷過維護一個已經離職的人的代碼的痛苦,一個方法寫老長,還有很多的ifelse,根本無法閱讀,更不知道代碼背后的含義,最重要的是沒有人可以問....

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

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

1、規范命名

命名是寫代碼中最頻繁的操作,比如類、屬性、方法、參數等。好的名字應當能遵循以下幾點:

見名知意

比如需要定義一個變量需要來計數

int i = 0;

名稱 i 沒有任何的實際意義,沒有體現出數量的意思,所以我們應當指明數量的名稱

int count = 0;

能夠讀的出來

如下代碼:

private String sfzh;private String dhhm;

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

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

private String idCardNo;private String phone;

2、規范代碼格式

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

好的代碼格式應當遵守以下幾點:

  • 合適的空格
  • 代碼對齊,比如大括號要對齊
  • 及時換行,一行不要寫太多代碼

好在現在開發工具支持一鍵格式化,可以幫助美化代碼格式。

3、寫好代碼注釋

在《代碼簡潔之道》這本書中作者提到了一個觀點,注釋得恰當用法是用來彌補我們在用代碼表達意圖時的失敗。換句話說,當無法通過讀代碼來了解代碼所表達的意思的時候,就需要用注釋來說明。

作者之所以這么說,是因為作者覺得隨著時間的推移,代碼可能會變動,如果不及時更新注釋,那么注釋就容易產生誤導,偏離代碼的實際意義。而不及時更新注釋的原因是,程序員不喜歡寫注釋。(作者很懂啊)

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

for (Integer id : ids) { if (id == 0) { continue; } //做其他事}

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

好的注釋應當滿足一下幾點:

  • 解釋代碼的意圖,說明為什么這么寫,用來做什么
  • 對參數和返回值注釋,入參代表什么,出參代表什么
  • 有警示作用,比如說入參不能為空,或者代碼是不是有坑
  • 當代碼還未完成時可以使用 todo 注釋來注釋
4、try catch 內部代碼抽成一個方法

try catch代碼有時會干擾我們閱讀核心的代碼邏輯,這時就可以把try catch內部主邏輯抽離成一個單獨的方法

如下圖是Eureka服務端源碼中服務下線的實現中的一段代碼

整個方法非常長,try中代碼是真正的服務下線的代碼實現,finally可以保證讀鎖最終一定可以釋放。

所以這段代碼其實就可以對核心的邏輯進行抽取。

protected boolean internalCancel(String appName, String id, boolean isReplication) { try { read.lock(); doInternalCancel(appName, id, isReplication); } finally { read.unlock(); } // 剩余代碼}private boolean doInternalCancel(String appName, String id, boolean isReplication) { //真正處理下線的邏輯}

5、方法別太長

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

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

所以一旦方法過長,可以嘗試將相同業務功能的代碼單獨抽取一個方法,最后在主方法中調用即可。

6、抽取重復代碼

當一份代碼重復出現在程序的多處地方,就會造成程序又臭又長,當這份代碼的結構要修改時,每一處出現這份代碼的地方都得修改,導致程序的擴展性很差。

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

7、多用return

在有時我們平時寫代碼的情況可能會出現if條件套if的情況,當if條件過多的時候可能會出現如下情況:

if (條件1) { if (條件2) { if (條件3) { if (條件4) { if (條件5) { System.out.println("三友的java日記"); } } } }}

面對這種情況,可以換種思路,使用return來優化

if (!條件1) { return;}if (!條件2) { return;}if (!條件3) { return;}if (!條件4) { return;}if (!條件5) { return;}System.out.println("三友的java日記");

這樣優化就感覺看起來更加直觀

8、if條件表達式不要太復雜

比如在如下代碼:

if (((StringUtils.isBlank(person.getName()) || "三友的java日記".equals(person.getName())) && (person.getAge() != Null && person.getAge() > 10)) && "漢".equals(person.getNational())) { // 處理邏輯}

這段邏輯,這種條件表達式乍一看不知道是什么,仔細一看還是不知道是什么,這時就可以這么優化

boolean sanyouOrBlank = StringUtils.isBlank(person.getName()) || "三友的java日記".equals(person.getName());boolean ageGreaterThanTen = person.getAge() != null && person.getAge() > 10;boolean isHanNational = "漢".equals(person.getNational());if (sanyouOrBlank && ageGreaterThanTen && isHanNational) { // 處理邏輯}

此時就很容易看懂if的邏輯了

9、優雅地參數校驗

當前端傳遞給后端參數的時候,通常需要對參數進場檢驗,一般可能會這么寫

@PostMappingpublic void addPerson(@RequestBody AddPersonRequest addPersonRequest) { if (StringUtils.isBlank(addPersonRequest.getName())) { throw new BizException("人員姓名不能為空"); } if (StringUtils.isBlank(addPersonRequest.getIdCardNo())) { throw new BizException("身份證號不能為空"); } // 處理新增邏輯}

這種寫雖然可以,但是當字段得多的時候,光校驗就占據了很長的代碼,不夠優雅。

針對參數校驗這個問題,有第三方庫已經封裝好了,比如hibernate-validator框架,只需要拿來用即可。

所以就在實體類上加@NotBlank、@NotNull注解來進行校驗

@Data@ToStringprivate class AddPersonRequest { @NotBlank(message = "人員姓名不能為空") private String name; @NotBlank(message = "身份證號不能為空") private String idCardNo; //忽略}

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

@PostMappingpublic void addPerson(@RequestBody @Valid AddPersonRequest addPersonRequest) { // 處理新增邏輯}

10、統一返回值

后端在設計接口的時候,需要統一返回值

{ "code":0, "message":"成功", "data":"返回數據"}

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

Spring中很多方法可以做到統一返回值,而不用每個方法都返回,比如基于AOP,或者可以自定義HandlerMethodReturnValueHandler來實現統一返回值。

11、統一異常處理

當你沒有統一異常處理的時候,那么所有的接口避免不了try catch操作。

@GetMapping("/{id}")public Result<T> selectPerson(@PathVariable("id") Long personId) { try { PersonVO vo = personService.selectById(personId); return Result.success(vo); } catch (Exception e) { //打印日志 return Result.error("系統異常"); }}

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

所以可以基于Spring提供的統一異常處理機制來完成。

12、盡量不傳遞null值

這個很好理解,不傳null值可以避免方法不支持為null入參時產生的空指針問題。

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

//示例1public void updatePerson(@Nullable Person person) { if (person == null) { return; } personService.updateById(person);}//示例2public void updatePerson(@NonNull Person person) { personService.updateById(person);}

13、盡量不返回null值

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

public Optional<Person> getPersonById(Long personId) { return Optional.ofNullable(personService.selectById(personId));}

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

14、日志打印規范

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

好的日志應該遵循以下幾點:

  • 可搜索性,要有明確的關鍵字信息
  • 異常日志需要打印出堆棧信息
  • 合適的日志級別,比如異常使用error,正常使用info
  • 日志內容太大不打印,比如有時需要將圖片轉成Base64,那么這個Base64就可以不用打印
15、統一類庫

在一個項目中,可能會由于引入的依賴不同導致引入了很多相似功能的類庫,比如常見的json類庫,又或者是一些常用的工具類,當遇到這種情況下,應當規范在項目中到底應該使用什么類庫,而不是一會用Fastjson,一會使用Gson。

16、盡量使用工具類

比如在對集合判空的時候,可以這么寫

public void updatePersons(List<Person> persons) { if (persons != null && persons.size() > 0) { }}

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

public void updatePersons(List<Person> persons) { if (!CollectionUtils.isEmpty(persons)) { }}

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

17、盡量不要重復造輪子

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

private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static String formatDateTime(Date date) { return DATE_TIME_FORMAT.format(date);}

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

一般對于這種已經有開源的項目并且已經做得很好的時候,比如Hutool,就可以把輪子直接拿過來用了。

18、類和方法單一職責

單一職責原則是設計模式的七大設計原則之一,它的核心意思就是字面的意思,一個類或者一個方法只做單一的功能。

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

這個類只干了一件事,那就是封裝http請求參數,向Nacos服務端發送請求,接收響應,這其實就是單一職責原則的體現。

當其它的地方需要向Nacos服務端發送請求時,只需要通過這個接口的實現,傳入參數就可以發送請求了,而不需要關心如何攜帶服務端鑒權參數、http請求參數如何組裝等問題。

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

繼承的弊端:

  • 靈活性低。java語言是單繼承的,無法同時繼承很多類,并且繼承容易導致代碼層次太深,不易于維護
  • 耦合性高。一旦父類的代碼修改,可能會影響到子類的行為

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

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

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

聚合和組合的區別就是,組合是當對象一創建的時候,就直接給屬性賦值,而聚合的方式可以通過set方式來設置。

組合:

public class OrderService { private UserService userService = new UserService();}

聚合:

public class OrderService { private UserService userService; public void setUserService(UserService userService) { this.userService = userService; }}

20、使用設計模式優化代碼

在平時開發中,使用設計模式可以增加代碼的擴展性。

比如說,當你需要做一個可以根據不同的平臺做不同消息推送的功能時,就可以使用策略模式的方式來優化。

設計一個接口:

public interface MessageNotifier { /** * 是否支持改類型的通知的方式 * * @param type 0:短信 1:app * @return */ boolean support(int type); /** * 通知 * * @param user * @param content */ void notify(User user, String content);}

短信通知實現:

@Componentpublic class SMSMessageNotifier implements MessageNotifier { @Override public boolean support(int type) { return type == 0; } @Override public void notify(User user, String content) { //調用短信通知的api發送短信 }}

app通知實現:

public class AppMessageNotifier implements MessageNotifier { @Override public boolean support(int type) { return type == 1; } @Override public void notify(User user, String content) { //調用通知app通知的api }}

最后提供一個方法,當需要進行消息通知時,調用notifyMessage,傳入相應的參數就行。

@Resourceprivate List<MessageNotifier> messageNotifiers;public void notifyMessage(User user, String content, int notifyType) { for (MessageNotifier messageNotifier : messageNotifiers) { if (messageNotifier.support(notifyType)) { messageNotifier.notify(user, content); } }}

假設此時需要支持通過郵件通知,只需要有對應實現就行。

21、不濫用設計模式

用好設計模式可以增加代碼的擴展性,但是濫用設計模式確是不可取的。

public void printPerson(Person person) { StringBuilder sb = new StringBuilder(); if (StringUtils.isNotBlank(person.getName())) { sb.append("姓名:").append(person.getName()); } if (StringUtils.isNotBlank(person.getIdCardNo())) { sb.append("身份證號:").append(person.getIdCardNo()); } // 省略 System.out.println(sb.toString());}

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

22、面向接口編程

在一些可替換的場景中,應該引用父類或者抽象,而非實現。

舉個例子,在實際項目中可能需要對一些圖片進行存儲,但是存儲的方式很多,比如可以選擇阿里云的OSS,又或者是七牛云,存儲服務器等等。所以對于存儲圖片這個功能來說,這些具體的實現是可以相互替換的。

所以在項目中,我們不應當在代碼中耦合一個具體的實現,而是可以提供一個存儲接口

public interface FileStorage { String store(String fileName, byte[] bytes);}

如果選擇了阿里云OSS作為存儲服務器,那么就可以基于OSS實現一個FileStorage,在項目中哪里需要存儲的時候,只要實現注入這個接口就可以了。

@Autowiredprivate FileStorage fileStorage;

假設用了一段時間之后,發現阿里云的OSS比較貴,此時想換成七牛云的,那么此時只需要基于七牛云的接口實現FileStorage接口,然后注入到IOC,那么原有代碼用到FileStorage根本不需要動,實現輕松的替換。

23、經常重構舊的代碼

隨著時間的推移,業務的增長,有的代碼可能不再適用,或者有了更好的設計方式,那么可以及時的重構業務代碼。

就拿上面的消息通知為例,在業務剛開始的時候可能只支持短信通知,于是在代碼中就直接耦合了短信通知的代碼。但是隨著業務的增長,逐漸需要支持app、郵件之類的通知,那么此時就可以重構以前的代碼,抽出一個策略接口,進行代碼優化。

24、null值判斷

空指針是代碼開發中的一個難題,作為程序員的基本修改,應該要防止空指針。

可能產生空指針的原因:

  • 數據返回對象為null
  • 自動拆箱導致空指針
  • rpc調用返回的對象可能為空格

所以在需要這些的時候,需要強制判斷是否為null。前面也提到可以使用Optional來優雅地進行null值判斷。

25、pojo類重寫toString方法

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

26、魔法值用常量表示

public void sayHello(String province) { if ("廣東省".equals(province)) { System.out.println("靚仔~~"); } else { System.out.println("帥哥~~"); }}

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

private static final String GUANG_DONG_PROVINCE = "廣東省";public void sayHello(String province) { if (GUANG_DONG_PROVINCE.equals(province)) { System.out.println("靚仔~~"); } else { System.out.println("帥哥~~"); }}

27、資源釋放寫到finally

比如在使用一個api類鎖或者進行IO操作的時候,需要主動寫代碼需釋放資源,為了能夠保證資源能夠被真正釋放,那么就需要在finally中寫代碼保證資源釋放。

如圖所示,就是CopyOnWriteArrayList的add方法的實現,最終是在finally中進行鎖的釋放。

28、使用線程池代替手動創建線程

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

  • 降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。
  • 提高響應速度。當任務到達時,任務可以不需要的等到線程創建就能立即執行。
  • 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統 的穩定性,使用線程池可以進行統一的分配,調優和監控。

所以為了達到更好的利用資源,提高響應速度,就可以使用線程池的方式來代替手動創建線程。

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

29、線程設置名稱

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

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

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

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

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

在消費者在從服務端拉取消息的時候,會單獨開一個線程,執行while循環,只要stopped狀態一直為false,那么就會一直循環下去,線程就一直會運行下去,拉取消息。

當消費者客戶端關閉的時候,就會將stopped狀態設置為true,告訴拉取消息的線程需要停止了。但是由于并發編程中存在可見性的問題,所以雖然客戶端關閉線程將stopped狀態設置為true,但是拉取消息的線程可能看不見,不能及時感知到數據的修改,還是認為stopped狀態設置為false,那么就還會運行下去。

針對這種可見性的問題,java提供了一個volatile關鍵字來保證線程間的可見性。

所以,源碼中就加了volatile關鍵字。

加了volatile關鍵字之后,一旦客戶端的線程將stopped狀態設置為true時候,拉取消息的線程就能立馬知道stopped已經是false了,那么再次執行while條件判斷的時候,就不成立,線程就運行結束了,然后退出。

31、考慮線程安全問題

在平時開發中,有時需要考慮并發安全的問題。

舉個例子來說,一般在調用第三方接口的時候,可能會有一個鑒權的機制,一般會攜帶一個請求頭token參數過去,而token也是調用第三方接口返回的,一般這種token都會有個過期時間,比如24小時。

我們一般會將token緩存到Redis中,設置一個過期時間。向第三方發送請求時,會直接從緩存中查找,但是當從Redis中獲取不到token的時候,我們都會重新請求token接口,獲取token,然后再設置到緩存中。

整個過程看起來是沒什么問題,但是實則隱藏線程安全問題。

假設當出現并發的時候,同時來兩個線程AB從緩存查找,發現沒有,那么AB此時就會同時調用token獲取接口。假設A先獲取到token,B后獲取到token,但是由于CPU調度問題,線程B雖然后獲取到token,但是先往Redis存數據,而線程A后存,覆蓋了B請求的token。

這下就會出現大問題,最新的token被覆蓋了,那么之后一定時間內token都是無效的,接口就請求不通。

針對這種問題,可以使用double check機制來優化獲取token的問題。

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

32、慎用異步

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

事務問題

一旦使用了異步,就會導致兩個線程不是同一個事務的,導致異常之后無法正常回滾數據。

cpu負載過高

之前有個小伙伴遇到需要同時處理幾萬調數據的需求,每條數據都需要調用很多次接口,為了達到老板期望的時間要求,使用了多線程跑,開了很多線程,此時會發現系統的cpu會飆升

意想不到的異常

還是上面的提到的例子,在測試的時候就發現,由于并發量激增,在請求第三方接口的時候,返回了很多錯誤信息,導致有的數據沒有處理成功。

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

33、減小鎖的范圍

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

比如CopyOnWriteArrayList的addAll方法的實現,lock.lock(); 代碼完全可以放到代碼的第一行,但是作者并沒有,因為前面判斷的代碼不會有線程安全的問題,不放到加鎖代碼中可以減少鎖搶占和占有的時間。

34、有類型區分時定義好枚舉

比如在項目中不同的類型的業務可能需要上傳各種各樣的附件,此時就可以定義好不同的一個附件的枚舉,來區分不同業務的附件。

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

35、遠程接口調用設置超時時間

比如在進行微服務之間進行rpc調用的時候,又或者在調用第三方提供的接口的時候,需要設置超時時間,防止因為各種原因,導致線程”卡死“在那。

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

36、集合使用應當指明初始化大小

比如在寫代碼的時候,經常會用到List、Map來臨時存儲數據,其中最常用的就是ArrayList和HashMap。但是用不好可能也會導致性能的問題。

比如說,在ArrayList中,底層是基于數組來存儲的,數組是一旦確定大小是無法再改變容量的。但不斷的往ArrayList中存儲數據的時候,總有那么一刻會導致數組的容量滿了,無法再存儲其它元素,此時就需要對數組擴容。所謂的擴容就是新創建一個容量是原來1.5倍的數組,將原有的數據給拷貝到新的數組上,然后用新的數組替代原來的數組。

在擴容的過程中,由于涉及到數組的拷貝,就會導致性能消耗;同時HashMap也會由于擴容的問題,消耗性能。所以在使用這類集合時可以在構造的時候指定集合的容量大小。

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

在開發中經常需要對JavaBean進行轉換,但是又不想一個一個手動set,比較麻煩,所以一般會使用屬性拷貝的一些工具,比如說Spring提供的BeanUtils來拷貝。不得不說,使用BeanUtils來拷貝屬性是真的舒服,使用一行代碼可以代替幾行甚至十幾行代碼,我也喜歡用。

但是喜歡歸喜歡,但是會帶來性能問題,因為底層是通過反射來的拷貝屬性的,所以盡量不要用BeanUtils來拷貝屬性。

比如你可以裝個JavaBean轉換的插件,幫你自動生成轉換代碼;又或者可以使用性能更高的MapStruct來進行JavaBean轉換,MapStruct底層是通過調用(settter/getter)來實現的,而不是反射來快速執行。

38、使用StringBuilder進行字符串拼接

如下代碼:

String str1 = "123";String str2 = "456";String str3 = "789";String str4 = str1 str2 str3;

使用 拼接字符串的時候,會創建一個StringBuilder,然后將要拼接的字符串追加到StringBuilder,再toString,這樣如果多次拼接就會執行很多次的創建StringBuilder,z執行toString的操作。

所以可以手動通過StringBuilder拼接,這樣只會創建一次StringBuilder,效率更高。

StringBuilder sb = new StringBuilder();String str = sb.append("123").append("456").append("789").toString();

39、@Transactional應指定回滾的異常類型

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

默認是只能回滾RuntimeException和Error異常,所以需要手動指定,比如指定成Expection等。

40、謹慎方法內部調用動態代理的方法

如下事務代碼

@Servicepublic class PersonService { public void update(Person person) { // 處理 updatePerson(person); } @Transactional(rollbackFor = Exception.class) public void updatePerson(Person person) { // 處理 }}

update調用了加了@Transactional注解的updatePerson方法,那么此時updatePerson的事務就是失效。

其實失效的原因不是事務的鍋,是由AOP機制決定的,因為事務是基于AOP實現的。AOP是基于對象的代理,當內部方法調用時,走的不是動態代理對象的方法,而是原有對象的方法調用,如此就走不到動態代理的代碼,就會失效了。

如果實在需要讓動態代理生效,可以注入自己的代理對象

@Servicepublic class PersonService { @Autowired private PersonService personService; public void update(Person person) { // 處理 personService.updatePerson(person); } @Transactional(rollbackFor = Exception.class) public void updatePerson(Person person) { // 處理 }}

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

查詢全字段有以下幾點壞處:

增加不必要的字段的網絡傳輸

比如有些文本的字段,存儲的數據非常長,但是本次業務使用不到,但是如果查了就會把這個數據返回給客戶端,增加了網絡傳輸的負擔

會導致無法使用到覆蓋索引

比如說,現在有身份證號和姓名做了聯合索引,現在只需要根據身份證號查詢姓名,如果直接select name 的話,那么在遍歷索引的時候,發現要查詢的字段在索引中已經存在,那么此時就會直接從索引中將name字段的數據查出來,返回,而不會繼續去查找聚簇索引,減少回表的操作。

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

Wrappers.query().select("name");

42、不循環調用數據庫

不要在循環中訪問數據庫,這樣會嚴重影響數據庫性能。

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

public List<PersonVO> selectPersons(List<Long> personIds) { List<PersonVO> persons = new ArrayList<>(personIds.size()); List<Person> personList = personMapper.selectByIds(personIds); for (Person person : personList) { PersonVO vo = new PersonVO(); PersonExt personExt = personExtMapper.selectById(person.getId()); // 組裝數據 persons.add(vo); } return persons;}

遍歷每個人員的基本信息,去數據庫查找。

正確的方法應該先批量查出來,然后轉成map:

public List<PersonVO> selectPersons(List<Long> personIds) { List<PersonVO> persons = new ArrayList<>(personIds.size()); List<Person> personList = personMapper.selectByIds(personIds); //批量查詢,轉換成Map List<PersonExt> personExtList = personExtMapper.selectByIds(person.getId()); Map<String, PersonExt> personExtMap = personExtList.stream().collect(Collectors.toMap(PersonExt::getPersonId, Function.identity())); for (Person person : personList) { PersonVO vo = new PersonVO(); //直接從Map中查找 PersonExt personExt = personExtMap.get(person.getId()); // 組裝數據 persons.add(vo); } return persons;}

43、用業務代碼代替多表join

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

而之所以會禁用,是因為join的效率比較低。

MySQL是使用了嵌套循環的方式來實現關聯查詢的,也就是for循環會套for循環的意思。用第一張表做外循環,第二張表做內循環,外循環的每一條記錄跟內循環中的記錄作比較,符合條件的就輸出,這種效率肯定低。

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

我們平時寫代碼由于各種因為,比如什么領導啊,項目經理啊,會一直催進度,導致寫代碼都來不及思考,怎么快怎么來,cv大法上線,雖然有心想寫好代碼,但是手確不聽使喚。所以我建議裝一個阿里的代碼規范插件,如果有代碼不規范,會有提醒,這樣就可以知道哪些是可以優化的了。

如果你有強迫癥,相信我,裝了這款插件,你的代碼會寫的很漂亮。

45、及時跟同事溝通

寫代碼的時候不能閉門造車,及時跟同事溝通,比如剛進入一個新的項目的,對項目工程不熟悉,一些技術方案不了解,如果上來就直接寫代碼,很有可能就會踩坑。

來源:https://mp.weixin.qq.com/s/mRivkLYxFC9fFyXkhpd4Wg

TAGS標簽:  100個  代碼  技巧  寫出  漂亮  100個寫代碼技巧(

歡迎分享轉載→http://m.avcorse.com/read-370628.html

Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號-5 TXT地圖HTML地圖XML地圖