寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

前言java

已經作完項目最近的幾個需求,在回顧本身所寫的代碼的時候,發現了有一些思考不夠深刻的地方,因而我寫了這篇文章總結一下。程序員

但願你們平常寫代碼多點思考,多點總結,加油!同時哪裏有不對的,也望指出。sql


1、複雜的邏輯條件,是否能夠調整順序,讓程序更高效呢。

假設業務需求是這樣:會員,第一次登錄時,須要發一條感謝短信。若是沒有通過思考,代碼直接這樣寫了數據庫

if(isUserVip && isFirstLogin){    sendMsg();}複製代碼

假設總共有 5 個請求,isUserVip 經過的有 3 個請求,isFirstLogin 經過的有 1 個請求。 那麼以上代碼,isUserVip 執行的次數爲 5 次,isFirstLogin 執行的次數也是 3 次,以下:編程

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

若是調整一下 isUserVip 和 isFirstLogin 的順序呢?後端

if(isFirstLogin && isUserVip ){    sendMsg();}複製代碼

isFirstLogin 執行的次數是 5 次,isUserVip 執行的次數是 1 次,以下:緩存

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

醬紫你的程序是否更高效呢?性能優化

2、你的程序是否不經意間建立了沒必要要的對象。

舉個粟子吧,判斷用戶會員是否處於有效期,一般有如下相似代碼:bash

//判斷用戶會員是否在有效期public boolean isUserVIPValid()
 {  Date now = new Date();  Calendar gmtCal = Calendar.getInstance();  
gmtCal.set(2019, Calendar.JANUARY, 1, 0, 0, 0);  Date beginTime = gmtCal.getTime(); 
 gmtCal.set(2020, Calendar.JANUARY, 1, 0, 0, 0);  Date endTime= gmtCal.getTime(); 
 return now.compareTo(beginTime) >= 0 && now.compareTo(endTime) <= 0;}複製代碼

可是呢,每次調用 isUserVIPValid 方法,都會建立 Calendar 和 Date 對象。其實吧,除了 New Date,其餘對象都是不變的,咱們能夠抽出全局變量避免建立了沒必要要的對象,從而提升程序效率,以下:網絡

public class Test {    private static final Date BEGIN_TIME;   
 private static final Date END_TIME;  
 static {        Calendar gmtCal = Calendar.getInstance();      
  gmtCal.set(2019, Calendar.JANUARY, 1, 0, 0, 0);      
  BEGIN_TIME = gmtCal.getTime();        
gmtCal.set(2020, Calendar.JANUARY, 1, 0, 0, 0);       
 END_TIME = gmtCal.getTime();    }    //判斷用戶會員是否在有效期    
public boolean isUserVIPValid() {        Date now = new Date();        
return now.compareTo(BEGIN_TIME) >= 0 && now.compareTo(END_TIME) <= 0;    }}複製代碼

3、查詢數據庫時,你有沒有查多了數據?

你們都知道,查庫是比較耗時的操做,尤爲數據量大的時候。因此,查詢 DB 時,咱們取所需就好,沒有必要大包大攬。

假設業務場景是這樣:查詢某個用戶是不是會員。曾經看過實現代碼是這樣。。。

List<Long> userIds = sqlMap.queryList("select userId from user where vip=1");
boolean isVip = userIds.contains(userId);複製代碼

爲何先把全部會有查出來,再判斷是否包含這個 useId,來肯定 useId 是不是會員呢?直接把 userId 傳進 sql,它不香嗎?以下:

Long userId = sqlMap.queryObject
("select userId from user where userId='userId' and vip='1' ")boolean isVip = userId!=null;

複製代碼

實際上,咱們除了把查詢條件都傳過去,避免數據庫查多餘的數據回來,還能夠經過select 具體字段代替 select*,從而使程序更高效。

4、加了一行通知類的代碼,總不能影響到主要流程吧。

假設業務流程這樣:須要在用戶登錄時,添加個短信通知它的粉絲。很容易想到的實現流程以下:

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

假設提供 sendMsgNotify 服務的系統掛了,或者調用 sendMsgNotify 失敗了,那麼用戶登錄就失敗了。。。

一個通知功能致使了登錄主流程不可用,明顯的撿了芝麻丟西瓜。那麼有沒有魚魚熊掌兼得的方法呢?有的,給發短信接口捕獲異常處理,或者另開線程異步處理,以下:

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

所以,咱們添加通知類等不是非主要,可降級的接口時,應該靜下心來考慮是否會影響主要流程,思考怎麼處理最好。

5、對空指針保持嗅覺,如使用 equals 比較時,常量或肯定值放左邊。

NullPointException 在 Java 世界早已司空見慣,咱們在寫代碼時,能夠三思然後寫,儘可能避免低級的空指針問題。

好比有如下業務場景,判斷用戶是不是會員,常常可見以下代碼:

boolean isVip = user.getUserFlag().equals("1");複製代碼

若是讓這個行代碼上生產環境,待君驀然回首,可能那空指針 bug,就在燈火闌珊處。顯然,這樣可能會產生空指針異常,由於 user.getUserFlag()多是 null。

怎樣避免空指針問題呢?把常量 1 放到左邊就能夠啦,以下:

boolean isVip = "1".equals(user.getUserFlag());複製代碼

6、你的關鍵業務代碼是否有日誌保駕護航?

關鍵業務代碼不管身處何地,都應該有足夠的日誌保駕護航。

好比:你實現轉帳業務,轉個幾百萬,而後轉失敗了,接着客戶投訴,而後你尚未打印到日誌,想一想那種水深火熱的困境下,你卻毫無辦法。。。

那麼,你的轉帳業務都須要那些日誌信息呢?至少,方法調用前,入參須要打印須要吧,接口調用後,須要捕獲一下異常吧,同時打印異常相關日誌吧,以下:

public void transfer(TransferDTO transferDTO)
{    log.info("invoke tranfer begin");    //打印入參    log.info("invoke tranfer,paramters:{}"
,transferDTO);    try {      res=  transferService.transfer(transferDTO);    }
catch(Exception e){     log.error("transfer fail,cifno:{},account:{}",
transferDTO.getCifno(),     transferDTO.getaccount())    
 log.error("transfer fail,exception:{}",e);    }    log.info("invoke tranfer end");    }複製代碼

除了打印足夠的日誌,咱們還須要注意一點是,日誌級別別混淆使用,別本該打印 info 的日誌,你卻打印成 error 級別,告警半夜三更催你起來排查問題就很差了。

7、對於行數比較多的函數,是否能夠劃分小函數來優化呢?

咱們在維護老代碼的時候,常常會見到一坨坨的代碼,有些函數幾百行甚至上千行,閱讀起來比較吃力。

假設如今有如下代碼

public class Test {    private String name;    
private Vector<Order> orders = new Vector<Order>();   
 public void printOwing() {        //print banner        
System.out.println("****************");        
System.out.println("*****customer Owes *****");        
System.out.println("****************");        //calculate totalAmount     
   Enumeration env = orders.elements();        double totalAmount = 0.0;    
    while (env.hasMoreElements()) {            Order order = (Order) env.nextElement();            totalAmount += order.getAmout();        }        //print details        System.out.println("name:" + name);        System.out.println("amount:" + totalAmount);    }}複製代碼

劃分爲功能單一的小函數後:

public class Test {    private String name;    private Vector<Order> 
orders = new Vector<Order>();    public void printOwing()
 {        //print banner        printBanner();       
 //calculate totalAmount        double totalAmount = getTotalAmount();        
//print details        printDetail(totalAmount);    }    
void printBanner(){        System.out.println("****************");      
  System.out.println("*****customer Owes *****");        
System.out.println("****************");    }    double getTotalAmount()
{        Enumeration env = orders.elements();        
double totalAmount = 0.0;        while (env.hasMoreElements())
 {            Order order = (Order) env.nextElement();            
totalAmount += order.getAmout();        }        return totalAmount;    }   
 void printDetail(double totalAmount){        System.out.println("name:" + name);     
   System.out.println("amount:" + totalAmount);    }}複製代碼

一個過於冗長的函數或者一段須要註釋才能讓人理解用途的代碼,能夠考慮把它切分紅一個功能明確的函數單元,並定義清晰簡短的函數名,這樣會讓代碼變得更加優雅。

Guide 哥:IDEA 對重構提供了很是友好的支持,我後面會在公衆號發一些重構的小技巧!

8、某些可變因素,如紅包皮膚等等,作成配置化是否會更好呢。

假如產品提了個紅包需求,聖誕節的時候,紅包皮膚爲聖誕節相關的,春節的時候,紅包皮膚等。

若是在代碼寫死控制,可有相似如下代碼:

if(duringChristmas){   img = redPacketChristmasSkin;}
else if(duringSpringFestival){   img =  redSpringFestivalSkin;}......複製代碼

若是到了元宵節的時候,運營小姐姐忽然又有想法,紅包皮膚換成燈籠相關的,這時候,是否是要去修改代碼了,從新發布了?

從一開始,實現一張紅包皮膚的配置表,將紅包皮膚作成配置化呢?更換紅包皮膚,只需修改一下表數據就行了。

9、多餘的 import 類,局部變量,沒引用是否是應該刪除

若是看到代碼存在沒使用的 import 類,沒被使用到的局部變量等,就刪掉吧,以下這些:

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

這些沒被引用的局部變量,若是沒被使用到,就刪掉吧,它又不是陳年的女兒紅,留着會愈加醇香。它仍是會一塊兒被編譯的,就是說它仍是耗着資源的呢。

10、查詢大表時,是否加了索引,你的 sql 走了索引嘛。

查詢數據量比較大的表時,咱們須要確認三點:

  • 你的表是否建了索引
  • 你的查詢 sql 是否命中索引
  • 你的 sql 是否還有優化餘地

通常狀況下,數據量超過 10 萬的表,就要考慮給表加索引了。哪些狀況下,索引會失效呢?like 通配符、索引列運算等會致使索引失效。有興趣的朋友能夠看一下我這篇文章。後端程序員必備:索引失效的十大雜症

11、你的方法到底應該返回空集合仍是 null 呢?

若是返回 null,調用方在忘記檢測的時候,可能會拋出空指針異常。返回一個空集合呢,就省去該問題了。

mybatis 查詢的時候,若是返回一個集合,結果爲空時也會返回一個空集合,而不是 null。

正例

public static List<UserResult> getUserResultList()
{    return Collections.EMPTY_LIST;}複製代碼

12、初始化集合時儘可能指定其大小

阿里開發手冊推薦了這一點!

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

假設你的 map 要存儲的元素個數是 15 個左右,最優寫法以下

//initialCapacity = 15/0.75+1=21 Map map = new HashMap(21); 
又由於hashMap的容量跟2的冪有關,因此能夠取32的容量 Map map = new HashMap(32);複製代碼

十3、查詢數據庫時,若是數據返回過多,考慮分批進行。

假設你的訂單表有 10 萬數據要更新狀態,不能一次性查詢全部未更新的訂單,要分批。

反例:

List<Order> list = sqlMap.queryList("select * from Order where status='0'");
for(Order order:list){  order.setStatus(1);  sqlMap.update(order);}複製代碼

正例:

Integer count = sqlMap.queryCount(select count(1) from Order where status ='0');
while(true){    int size=sqlMap.batchUpdate(params);    if(size<500){        break;    }}

複製代碼

十4、你的接口是否考慮到冪等性,併發狀況呢?

冪等性是什麼? 一次和屢次請求某一個資源對於資源自己應該具備一樣的結果。就是說,其任意屢次執行對資源自己所產生的影響均與一次執行的影響相同。

爲何須要冪等性?

  • 用戶在 APP 上連續點擊了屢次提交訂單,總不能生成多個訂單吧
  • 用戶由於網絡卡了,連續點擊發送消息,接受者總不能收到重複的同一條消息吧。

假設有業務場景:

用戶點擊下載按鈕,系統開始下載文件,用戶再次點擊下載,會提示文件正在下載中。

有一部分人會這樣實現:

Integer count = sqlMap.selectCount("select count(1) from excel where state=1");
if(count<=0){    Excel.setStatus(1);    updateExcelStatus();    downLoadExcel();}
else{    "文件正在下載中"}複製代碼

咱們能夠看一下,兩個請求過來可能會有什麼問題?

寫代碼有這些想法,同事纔不會認爲你是複製粘貼程序員

執行流程:

  • 第一步,A 查詢沒有下載中的文件。
  • 第二步,B 查詢沒有下載中的文件。
  • 第三步,A 開始下載文件
  • 第四部,B 開始下載文件

顯然,這樣有問題,同時兩個文件在下載了。正確的實現方式呢?

if(updateExcelStatus(1){    downLoadExcel();}else{    "文件正在下載中"}複製代碼

十5、用一個私有構造器強化你的工具類,此不美哉?

工具類的方法都是靜態方法,經過類來直接調用便可。可是有些調用方可能會先實例化,再用對象去調用,而這就很差了。怎麼避免這種狀況,讓你的工具類到達可控狀態呢,添加私有構造器

public class StringUtis{       private StringUtis(){} ///私有構造類,防止意外實例出現     
  public static bool validataString(String str){       }}複製代碼

十6、基本不變的用戶數據,緩存起來,性能是否有所提高呢

假設你的接口須要查詢不少次數據庫,獲取到各中數據,而後再根據這些數據進行各類排序等等操做,這一系列猛如虎的操做下來,接口性能確定很差。典型應用場景好比:直播列表這些。

那麼,怎麼優化呢?剖析你排序的各部分數據,實時變的數據,繼續查 DB,不變的數據,如用戶年齡這些,搞個定時任務,把它們從 DB 拉取到緩存,直接走緩存。

所以,這個點的思考就是,在恰當地時機,適當的使用緩存。

我最近整理了一些java架構知識,有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化,併發編程這些成爲架構師必備的知識體系,須要的小夥伴能夠掃一下面的二維碼免費獲取


相關文章
相關標籤/搜索