Java開發經驗——阿里巴巴編碼規范經驗總結2

摘要

這篇文章是關于Java開發中阿里巴巴編碼規范的經驗總結。它強調了避免使用Apache BeanUtils進行屬性復制,因為它效率低下且類型轉換不安全。推薦使用Spring BeanUtils、Hutool BeanUtil、MapStruct或手動賦值等替代方案。文章還指出不應在視圖模板中加入復雜邏輯運算,應明確MVC架構各層的職責。此外,還涉及數據結構初始化應指定大小、正則表達式的預編譯、避免通過catch處理某些RuntimeException異常、finally塊中資源關閉的正確方式以及防止NPE的多種方法。

1. 【強制】避免用 ApacheBeanutils 進行屬性的 copy。

不推薦使用 Apache Commons BeanUtils 工具來進行對象屬性復制(如 BeanUtils.copyProperties),因為它效率低、性能差、類型轉換不安全,在生產環境中容易成為性能瓶頸。

Apache BeanUtils 是通過反射 + 內省(Introspector)+ 字符串轉換來做屬性 copy,性能非常低,不適合在高并發或大量對象轉換場景中使用。

1.1. 屬性賦值推薦方案

方案

優勢

場景

Spring BeanUtils

性能略優于 Apache,但仍是反射

適合小量級對象拷貝

Hutool BeanUtil

性能高,支持深拷貝、自定義字段映射

推薦在工具類中統一封裝

MapStruct

編譯期生成拷貝代碼(無反射,極快)

推薦在 DDD 中的 DO <-> DTO 映射

手動賦值

最安全、最清晰

小對象或關鍵轉換邏輯

ModelMapper / Dozer(不推薦)

仍是反射,配置復雜,性能低

不推薦使用

2. 【強制】不要在視圖模板中加入任何復雜的邏輯運算。

在 MVC 架構的具體實現中,比如 Spring Boot 項目中,我們常見的結構包括:

  • Controller(控制器)
  • Service(服務/業務邏輯層)
  • DAO(數據訪問層,也叫 Mapper、Repository)

下面是這三層的職責和理解方式,結合“不要在視圖中寫復雜邏輯”的那條建議,進一步深化層次的劃分:

2.1. Controller:控制層

職責:

  • 接收 HTTP 請求參數;
  • 調用 Service 進行處理;
  • 封裝和返回響應數據(Response);
  • 做參數校驗、權限判斷、日志記錄等外圍操作。

不要做的事:

  • 不要寫業務邏輯;
  • 不要操作數據庫;
  • 不要做復雜的流程判斷或數據處理。

示例:

@PostMapping("/user/upgrade")
public Response<Void> upgradeUser(@RequestBody UserUpgradeRequest request) {userService.upgradeUserToVip(request.getUserId());return Response.success();
}

2.2. Service:業務邏輯層

職責:

  • 實現具體業務邏輯,如“升級用戶為 VIP”、“扣減庫存”、“發送通知”等;
  • 調用多個 DAO、封裝業務判斷流程;
  • 做事務控制(@Transactional);
  • 組裝處理結果返回 Controller。

不要做的事:

  • 不要和 Web 框架(如 Servlet、HttpRequest)耦合;
  • 不要拼 SQL,不直接操作數據庫。

示例:

public void upgradeUserToVip(Long userId) {UserDO user = userDao.findById(userId);if (user == null || user.isVip()) {throw new BizException("用戶不存在或已是VIP");}user.setVip(true);userDao.update(user);notifyService.sendVipNotification(user);
}

2.3. DAO(Mapper/Repository):數據訪問層

職責:

  • 直接與數據庫交互;
  • 封裝 SQL 查詢(或通過 MyBatis/JPA 映射);
  • 只做增刪改查操作;
  • 返回實體對象,不做業務判斷。

不要做的事:

  • 不要處理業務邏輯;
  • 不要做流程判斷;
  • 不要拼接復雜結果(如組裝響應對象)。

示例:

@Mapper
public interface UserDao {UserDO findById(Long userId);int update(UserDO user);
}

2.4. 總結類比:工廠分工

層級

類比

職責

Controller

前臺接待

接收客戶請求,轉交到內部處理

Service

經理

安排工人干活,處理流程,判斷異常

DAO

工人

操作數據庫,搬原材料,不決策

2.5. MVC 和模板邏輯的對應關系

  • 視圖(View) = 頁面模板、前端:只能展示數據,不參與 Controller、Service、DAO 的職責
  • 所以復雜判斷、業務數據準備都應該在 Service/Controller 中完成,模板中直接展示結果即可

3. 【強制】任何數據結構的構造或初始化,都應指定大小,避免數據結構無限增長吃光內存。

3.1. 這條建議的核心思想是

在使用如集合(List、Map、Set 等)這類可擴展數據結構時,應盡可能“預估其大小”并“顯式設置初始容量”,從而避免它們在運行中頻繁擴容、內存抖動,甚至 OOM(內存溢出)的問題。提前預估數據量,合理初始化集合容量,是性能優化與內存安全的重要實踐。

ArrayList
HashMap
HashSet
ConcurrentHashMap
StringBuilder
自定義緩存、隊列等

這些類的背后都依賴一個數組或哈希桶來存儲數據,如果你沒有指定容量,它們會用默認大小初始化,然后在插入過程中自動擴容(重新開數組、拷貝數據等)。

3.2. 為什么要指定大小?

3.2.1. 不指定容量的風險:

  • 頻繁擴容: 每次容量不夠都要重新分配數組,拷貝舊數據 ? 性能開銷大;
  • 內存浪費: 擴容步長不是線性的,可能會分配遠超實際需要的空間;
  • 內存溢出(OOM): 在循環里構造數據結構沒有設置上限 ? 無限增長,吃光堆內存。

3.2.2. 指定容量的好處:

  • 減少擴容次數: 提高性能;
  • 控制內存: 限制最大容量,防止意外 OOM;
  • 體現程序邊界意識: 編碼更健壯。

3.3. 數據結構設置初始值示例對比

3.3.1. 不指定大小(有性能隱患):

List<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {list.add("item" + i);
}

默認容量是 10,之后 1.5 倍擴容 ? 至少擴容 10+ 次,代價很高

3.3.2. 指定大小(性能友好):

List<String> list = new ArrayList<>(100000);
for (int i = 0; i < 100000; i++) {list.add("item" + i);
}

只創建一次內部數組,避免擴容

3.4. 延伸到場景

數據結構

默認容量

推薦用法

ArrayList

10

new ArrayList<>(預計數量)

HashMap

16

new HashMap<>(預計數量 / 負載因子 + 1)

StringBuilder

16

new StringBuilder(預計字符串長度)

ConcurrentHashMap

16

new ConcurrentHashMap<>(預計大小)

4. 【強制】在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度。

說明:不要在方法體內定義:Pattern pattern = Pattern.compile("規則");

正則表達式在使用時,如果每次都重新編譯,會嚴重影響性能。應該使用預編譯(Pattern.compile(...))方式,將正則表達式提前編譯好并重復使用。

在 Java 中使用正則時,一般有兩種方式:

4.1. 每次都編譯(效率低)

boolean isMatch = "abc123".matches("\\w+");

內部其實相當于:

Pattern.compile("\\w+").matcher("abc123").matches();

這會每次調用都重新編譯正則表達式,開銷很大,尤其在循環或高并發下。

4.2. 預編譯后復用(推薦)

private static final Pattern PATTERN = Pattern.compile("\\w+");boolean isMatch = PATTERN.matcher("abc123").matches();

正則表達式只在類加載時編譯一次,后續調用直接復用,提高性能。

4.3. 使用場景

場景

是否推薦預編譯

單次用、不頻繁

可以臨時用 .matches()

多次校驗、循環中用

必須預編譯

高并發服務接口中

必須預編譯

工具類/公共方法

強烈建議預編譯并靜態緩存

4.4. 總結

  • 編譯正則是耗時操作
  • 多次使用時,一定要用 Pattern.compile(...) 并緩存起來;
  • 正則預編譯 = 性能優化 + 好習慣。

5. 【強制】Java 類庫中定義的可以通過預檢查方式規避的 RuntimeException 異常不應該通過 catch 的方式來處理,比如:NullPointerException,IndexOutOfBoundsException 等等。

try-catch 是用來處理不可預知的異常情況,不是用來“代替 if 判斷”的。對于 Java 類庫中常見的 RuntimeException(運行時異常),如果我們可以在代碼運行前通過邏輯“預檢查”避免它的發生,就不應該依賴 try-catch 來處理它。

5.1. 舉幾個典型例子

5.1.1. 不推薦的做法(用 catch 捕獲 NPE):

try {System.out.println(user.getName());
} catch (NullPointerException e) {// 捕獲空指針異常System.out.println("user 為空");
}

5.1.2. 推薦的做法(用 if 判斷提前規避):

if (user != null) {System.out.println(user.getName());
} else {System.out.println("user 為空");
}

5.2. 為什么不推薦用 catch 處理這些異常?

  1. 這類異常不是業務異常,而是代碼邏輯錯誤:出現 NullPointer、數組越界等,說明你的代碼邏輯寫得有問題,不是正常的“可恢復”情況。
  2. catch 成本高,影響性能:try-catch 的異常捕獲機制在 JVM 中性能是開銷較大的(尤其是頻繁拋異常的情況)。
  3. 可讀性變差,調試困難:濫用 catch 會把真正的問題掩蓋,調試困難,也不利于代碼維護。

5.3. 適用的異常類型(不建議 catch)

異常類

說明

NullPointerException

空指針異常,應通過非空判斷避免

IndexOutOfBoundsException

下標越界,應判斷下標是否合法

ClassCastException

類型轉換錯誤,應先 instanceof判斷

IllegalArgumentException

參數非法,應通過參數校驗處理

5.4. 異常捕獲正確的原則

  • 能通過邏輯避免的異常,不要 try-catch
  • RuntimeException 更多是一種編碼警告,不是業務流程的一部分
  • 只在頂層兜底或做日志監控時統一捕獲這些異常

6. 【強制】finally 塊必須對資源對象、 流對象進行關閉,有異常也要做 try-catch。

無論是否發生異常,finally 塊中一定要確保資源被正確關閉,且關閉操作本身也要加 try-catch,避免二次異常導致資源未釋放。

6.1. 正確的使用方式示例:

自 Java 7 起,Java 提供了 try-with-resources 語法,它能夠自動關閉實現了 AutoCloseableCloseable 接口的資源(如 InputStream)。使用該語法,可以消除手動管理資源關閉的復雜性,并自動處理 close() 方法可能拋出的異常。

try (InputStream in = new FileInputStream("data.txt")) {// 讀文件邏輯
} catch (IOException e) {e.printStackTrace(); // 異常處理
}

6.2. 錯誤的示例(不捕獲關閉異常):

finally {in.close(); // 如果這里拋出 IOException,整個異常流程會被覆蓋
}

6.3. 適用范圍:

這條規范適用于所有需要關閉或釋放的資源類,例如:

  • IO 流(InputStream、OutputStream、Reader、Writer 等)
  • 數據庫連接(Connection、Statement、ResultSet)
  • 網絡資源(Socket、HttpURLConnection)
  • 文件句柄
  • 線程池(ExecutorService 的 shutdown
  • 鎖(Lock.unlock()

7. 【推薦】防止 NPE,是程序員的基本修養,注意 NPE 產生的場景

1)返回類型為基本數據類型,return 包裝數據類型的對象時,自動拆箱有可能產生 NPE,反例:public int method() { return Integer 對象; },如果為 null,自動解箱拋 NPE。
2)數據庫的查詢結果可能為 null。
3)集合里的元素即使 isNotEmpty,取出的數據元素也可能為 null。
4)遠程調用返回對象時,一律要求進行空指針判斷,防止 NPE。
5)對于 Session 中獲取的數據,建議進行 NPE 檢查,避免空指針。
6)級聯調用 obj.getA().getB().getC();一連串調用,易產生 NPE。正例: 使用 JDK8 的 Optional 類來防止 NPE 問題。

7.1. 常見的 NPE 產生場景

7.1.1. 訪問未初始化的對象

當你嘗試訪問一個未初始化的對象(即其值為 null)時,通常會拋出 NPE。

String str = null;
int length = str.length();  // NPE: str 是 null,無法調用 length()

7.1.2. 調用 null 對象的實例方法

如果對象為 null,直接調用其方法會導致空指針異常。

MyClass obj = null;
obj.someMethod();  // NPE: obj 是 null,無法調用 someMethod()

7.1.3. 嘗試訪問 null 數組元素

null 數組嘗試訪問元素時也會拋出 NPE。

String[] arr = null;
String element = arr[0];  // NPE: arr 是 null,無法訪問元素

7.1.4. 傳遞 null 給不接受 null 的方法

有些方法要求傳入非 null 的參數,如果傳入 null,可能會觸發 NPE。

public void printLength(String str) {
System.out.println(str.length());  // 如果 str 為 null,將引發 NPE
}

7.1.5. 鏈式調用中的空指針

在鏈式調用中,如果某一環節返回了 null,而后續還對其進行方法調用,就會導致 NPE。

Person person = getPerson();
int age = person.getAddress().getCity().getZipCode();  // 如果 person 或 address 為 null,則會 NPE

7.2. 如何防止NPE問題?

7.2.1. 避免使用 null

盡量避免使用 null,特別是在可能觸發 NPE 的地方。可以使用 Optional 來表示可能為空的值。

Optional<String> optionalStr = Optional.ofNullable(str);
optionalStr.ifPresent(s -> System.out.println(s.length()));  // 安全訪問

7.2.2. 空值檢查

在調用對象的方法之前,先檢查對象是否為 null

if (str != null) {System.out.println(str.length());  // 只有 str 非 null 時才調用方法
} else {System.out.println("str is null");
}

7.2.3. 使用默認值

如果方法或字段值可能為 null,考慮使用默認值或替代值。

String str = Optional.ofNullable(inputString).orElse("default value");

7.2.4. 適用斷言和工具庫

通過工具庫如 Apache Commons Lang 提供的 StringUtilsObjectUtils 等可以避免手動編寫空值檢查代碼,減少 NPE 風險。

StringUtils.isNotEmpty(str);  // 不會拋出空指針異常

7.2.5. 使用 @NonNull@Nullable 注解

通過注解可以清楚標明方法參數或返回值是否可以為空,這有助于避免因不清楚空指針約束導致的 NPE。

public void processString(@NonNull String str) {// str 必須不為 null
}

7.2.6. 避免深層嵌套的鏈式調用

通過設計合理的 API 接口或引入中間變量,避免深層次的鏈式調用,降低因某一環節為 null 導致的 NPE 風險。

Address address = person != null ? person.getAddress() : null;
if (address != null) {// 安全地訪問 address
}

博文參考

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/80382.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/80382.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/80382.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java大師成長計劃之第18天:Java Memory Model與Volatile關鍵字

&#x1f4e2; 友情提示&#xff1a; 本文由銀河易創AI&#xff08;https://ai.eaigx.com&#xff09;平臺gpt-4o-mini模型輔助創作完成&#xff0c;旨在提供靈感參考與技術分享&#xff0c;文中關鍵數據、代碼與結論建議通過官方渠道驗證。 在Java多線程編程中&#xff0c;線程…

js前端分片傳輸大文件+mongoose后端解析

最近一直在完善mongoose做webserver的項目&#xff0c;其中程序升級要通過前端傳輸升級包到服務器。 因為第一次寫前端代碼&#xff0c;分片傳輸的邏輯&#xff0c;網上一堆&#xff0c;大同小異&#xff0c;而且版本啊&#xff0c;API不一致的問題&#xff0c;導致頭疼的很。后…

MiniMind:3塊錢成本 + 2小時!訓練自己的0.02B的大模型。minimind源碼解讀、MOE架構

大家好&#xff0c;我是此林。 目錄 1. 前言 2. minimind模型源碼解讀 1. MiniMind Config部分 1.1. 基礎參數 1.2. MOE配置 2. MiniMind Model 部分 2.1. MiniMindForCausalLM: 用于語言建模任務 2.2. 主干模型 MiniMindModel 2.3. MiniMindBlock: 模型的基本構建塊…

引言:Client Hello 為何是 HTTPS 安全的核心?

當用戶在瀏覽器中輸入 https:// 時&#xff0c;看似簡單的操作背后&#xff0c;隱藏著一場加密通信的“暗戰”。Client Hello 作為 TLS 握手的首個消息&#xff0c;不僅決定了后續通信的加密強度&#xff0c;還可能成為攻擊者的突破口。據統計&#xff0c;超過 35% 的網站因 TL…

Dockerfile 完全指南:從入門到最佳實踐

Dockerfile 完全指南&#xff1a;從入門到最佳實踐 1. Dockerfile 簡介與作用 Dockerfile 是一個文本文件&#xff0c;包含了一系列用于構建 Docker 鏡像的指令。它允許開發者通過簡單的指令定義鏡像的構建過程&#xff0c;實現自動化、可重復的鏡像構建。 主要作用&#xf…

Python httpx庫終極指南

一、發展歷程與技術定位 1.1 歷史演進 起源&#xff1a;httpx 由 Encode 團隊開發&#xff0c;于 2019 年首次發布&#xff0c;目標是提供一個現代化的 HTTP 客戶端&#xff0c;支持同步和異步操作&#xff0c;并兼容 HTTP/1.1 和 HTTP/2。背景&#xff1a; requests 庫雖然功…

app加固

1、什么是加固? 我們之前講的逆向,大多數都是用加密算法去加密一些明文字符串,然后把得到的結果用 Base64、Hex等進行編碼后提交。加固其實也一樣&#xff0c;只不過他通常加密的是 dex文件而已。但是 dex 文件加密以后&#xff0c;安卓系統是沒法直接運行的。所以加固的核心&…

Win全兼容!五五 Excel Word 轉 PDF 工具解決多場景轉換難題

各位辦公小能手們&#xff01;今天給你們介紹一款超牛的工具——五五Excel Word批量轉PDF工具V5.5版。這玩意兒專注搞批量格式轉換&#xff0c;能把Excel&#xff08;.xls/.xlsx&#xff09;和Word&#xff08;.doc/.docx&#xff09;文檔唰唰地變成PDF格式。 先說說它的核心功…

springCloud/Alibaba常用中間件之Nacos服務注冊與發現

文章目錄 SpringCloud Alibaba:依賴版本補充六、Nacos:服務注冊與發現1、下載安裝Nacos2、服務注冊1. 導入依賴(這里以服務提供者為例)2. 修改配置文件和主啟動類3. 創建業務類4. 測試 3.服務映射1. 導入依賴2. 修改配置文件和主啟動類3. 創建業務類和RestTemplate配置類用來提…

uniapp中score-view中的文字無法換行問題。

項目場景&#xff1a; 今天遇到一個很惡心的問題&#xff0c;uniapp中的文字突然無法換行了。得..就介樣 原因分析&#xff1a; 提示&#xff1a;經過一fan研究后發現 scroll-view為了能夠橫向滾動設置了white-space: nowrap; 強制不換行 解決起來最先想到的是&#xff0c;父…

【STM32 學習筆記】I2C通信協議

注&#xff1a;通信協議的設計背景 3:00~10:13 I2C 通訊協議(Inter&#xff0d;Integrated Circuit)是由Phiilps公司開發的&#xff0c;由于它引腳少&#xff0c;硬件實現簡單&#xff0c;可擴展性強&#xff0c; 不需要USART、CAN等通訊協議的外部收發設備&#xff0c;現在被廣…

【網絡原理】數據鏈路層

目錄 一. 以太網 二. 以太網數據幀 三. MAC地址 四. MTU 五. ARP協議 六. DNS 一. 以太網 以太網是一種基于有線或無線介質的計算機網絡技術&#xff0c;定義了物理層和數據鏈路層的協議&#xff0c;用于在局域網中傳輸數據幀。 二. 以太網數據幀 1&#xff09;目標地址 …

控制臺打印帶格式內容

1. 場景 很多軟件會在控制臺打印帶顏色和格式的文字&#xff0c;需要使用轉義符實現這個功能。 2. 詳細說明 2.1.轉義符說明 樣式開始&#xff1a;\033[參數1;參數2;參數3m 可以多個參數疊加&#xff0c;若同一類型的參數&#xff08;如字體顏色&#xff09;設置了多個&…

[6-2] 定時器定時中斷定時器外部時鐘 江協科技學習筆記(41個知識點)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 V 30 31 32 33 34 35 36 37 38 39 40 41

數據庫的脫敏策略

數據庫的脫敏策略&#xff1a;就是屏蔽敏感的數據 脫敏策略三要求&#xff1a; &#xff08;1&#xff09;表對象 &#xff08;2&#xff09;生效條件&#xff08;脫敏列、脫敏函數&#xff09; &#xff08;3&#xff09;二元組 常見的脫敏策略規則&#xff1a; 替換、重排、…

Python序列化的學習筆記

1. Npy&Numpy O4-mini-Cursor&#xff1a;如果.npy文件里包含了「Python對象」而非純數值數組時&#xff0c;就必須在加載時加上allow_pickleTrue。

[思維模式-27]:《本質思考力》-7- 逆向思考的原理與應用

目錄 一、什么是逆向思考 1.1、逆向思考的六大核心思維模式 1.2、逆向思考的四大實踐方法 1. 假設倒置法 2. 缺陷重構法 3. 用戶反推法 4. 規則解構法 1.3、逆向思考的經典案例庫 1. 商業創新&#xff1a;從“賣產品”到“賣服務” 2. 用戶體驗&#xff1a;從“功能滿…

在python中,為什么要引入事件循環這個概念?

在Python中&#xff0c;事件循環&#xff08;Event Loop&#xff09;是異步編程的核心機制&#xff0c;它的引入解決了傳統同步編程模型在高并發場景下的效率瓶頸問題。以下從技術演進、性能優化和編程范式三個角度&#xff0c;探討這一概念的必要性及其價值。 一、同步模型的局…

Taccel:一個高性能的GPU加速視觸覺機器人模擬平臺

觸覺感知對于實現人類水平的機器人操作能力至關重要。而視覺觸覺傳感器&#xff08;VBTS&#xff09;作為一種有前景的解決方案&#xff0c;通過相機捕捉彈性凝膠墊的形變模式來感知接觸的方式&#xff0c;為視觸覺機器人提供了高空間分辨率和成本效益。然而&#xff0c;這些傳…

oracle 會話管理

會話管理 1&#xff1a;查看當前所有用戶的會話(SESSION)&#xff1a; SELECT * FROM V S E S S I O N W H E R E U S E R N A M E I S N O T N U L L O R D E R B Y L O G O N T I M E , S I D ; 其中 O r a c l e 內部進程的 U S E R N A M E 為空 2 &#xff1a;查看當前…