28個Java開發常用規範技巧總結
1、類的命名使用駝峰式命名的規範。
例如:UserService,但是以下情景例外:DO / BO / PO / DTO / VO。
例如說:UserPO,StudentPO(PO,VO,DTO,等這類名詞需要全大寫)
@Data @Builder public class CustomBodyDTO { private String name; private String idCode; private String status; }
2、如果在模組或者介面,類,方法中使用了設計模式,那麼請在命名的時候體現出來。
例如說:TokenFactory,LoginProxy等。
public class TokenFactory { public TokenDTO buildToken(LoginInfo loginInfo) { String token = UUID.randomUUID().toString(); TokenDTO tokenDTO = TokenDTO.builder() .token(token) .createTime(LocalDateTime.now()) .build(); String redisKey = RedisKeyBuilder.buildTokenKey(token); redisService.setObject(redisKey, loginInfo, Timeout.ONE_DAY * 30 * 2); log.info("建立token成功|loginInfo={}", loginInfo.toString()); return tokenDTO; } }
3、Object 的 equals 方法容易拋空指標異常。
從原始碼來進行分析equals方法是屬於Object類的,如果呼叫方為null,那麼自然在執行的時候會丟擲空指標異常的情況。
object類中的原始碼:
public boolean equals(Object obj) { return (this == obj); }
為了避免這種現況出現,在比對的時候儘量將常量或者有確定值的物件置前。
例如說:
正確:“test”.equals(object); 錯誤:object.equals(“test”);
4、對於所有相同型別的包裝類進行比較的時候,都是用equal來進行操作。
對於Integer類來說,當相應的變數數值範圍在-128到127之間的時候,該物件會被儲存在IntegerCache.cache裡面,因此會有物件複用的情況發生。
所以對於包裝類進行比較的時候,最好統一使用equal方法。
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} } public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
5、所有的pojo類中的屬性最好統一使用包裝類屬性型別資料。RPC方法的返回值和引數都統一使用包裝類資料。區域性變數中使用基本的資料型別。
對於實際的應用場景來說,例如說一個學生類,當我們設定裡面的成績欄位為int型別的時候,如果學生沒有考試,那麼這個成績欄位應該為空,但是int預設會賦值為0,那麼這個時候使用基本資料型別就容易產生誤區,到底是考了0分,還是說沒有參加考試。
如果換成使用包裝類Integer型別的話,就可以通過null值來進行區分了。
6、當pojo類在進行編寫的時候要重寫相應的toString方法,如果該pojo中繼承了另外的一個pojo類,那麼請在相應的tostring函式中加入super.toString()方法。
通過重寫toString方法有利於在日誌輸出的時候檢視相應物件的屬性內容進行逐一分析,對於一些有繼承關係的物件而言,加入了super.toString方法更加有助於對該物件的理解和分析。
7、在pojo的getter和setter方法裡面,不要增加業務邏輯的程式碼編寫,這樣會增加問題排查的難度。
正確做法:
public class User { private Integer id; private String username; public Integer getId() { return id; } public User setId(Integer id) { this.id = id; return this; } public String getUsername() { return username; } public User setUsername(String username) { this.username = username; return this; } }
錯誤做法:
public class User { private Integer id; private String username; public Integer getId() { return id; } public User setId(Integer id) { this.id = id; return this; } public String getUsername() { return "key-prefix-"+username; } public User setUsername(String username) { this.username = "key-prefix-"+username; return this; } }
8、final 可以宣告類、成員變數、方法、以及本地變數。
下列情況使用 final 關鍵字:
-
不允許被繼承的類,如:String 類。
-
不允許修改引用的域物件,如:POJO 類的域變數。
-
不允許被重寫的方法,如:POJO 類的 setter 方法。
-
不允許執行過程中重新賦值的區域性變數。
-
避免上下文重複使用一個變數,使用 final 描述可以強制重新定義一個變數,方便更好地進行重構。
9、對於任何類而言,只要重寫了equals就必須重寫hashcode。
舉例說明:
1)HashSet在儲存資料的時候是儲存不重複物件的,這些物件在進行判斷的時候需要依賴hashcode和equals方法,因此需要重寫。
2)在自定義物件作為key鍵時,需要重寫hashcode和equals方法,例如說String類就比較適合用於做key來使用。
10、不要在 foreach 迴圈裡進行元素的 remove/add 操作。
remove 元素請使用 Iterator方式,如果併發操作,需要對 Iterator 物件加鎖。
Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if (刪除元素的條件) { iterator.remove(); } }
11、使用HashMap的時候,可以指定集合的初始化大小。
例如說,HashMap裡面需要存放10000個元素,但是由於沒有進行初始化大小操作,所以在新增元素的時候,hashmap的內部會一直在進行擴容操作,影響效能。
那麼為了減少擴容操作,可以在初始化的時候將hashmap的大小設定為:已知需要儲存的大小/負載因子(0.75)+1
HashMap hashMap=new HashMap<>(13334);
12、Map類集合中,K/V對於null型別儲存的情況:
集合名稱 | key | value | 說明 |
---|---|---|---|
HashMap | 允許為null | 允許為null | 執行緒不安全 |
TreeMap | 不允許為null | 允許為null | 執行緒不安全 |
HashTable | 不允許為null | 不允許為null | 執行緒安全 |
ConcurrentHashMap | 不允許為null | 不允許為null | 執行緒安全 |
13、可以利用 Set 元素唯一的特性,可以快速對一個集合進行去重操作,避免使用 List 的contains 方法進行遍歷、對比、去重操作。
通關觀察可以發現,HashSet底層通過將傳入的值再傳入到一個HashMap裡面去進行操作,進入到HashMap裡面之後,會先通過呼叫該物件的hashcode來判斷是否有重複的值,如果有再進行equals判斷,如果沒有相同元素則插入處理。
public boolean add(E e) { return map.put(e, PRESENT)==null; }
14、執行緒池不允許使用 Executors 去建立,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確執行緒池的執行規則,規避資源耗盡的風險。
錯誤做法:
ExecutorService executors = Executors.newSingleThreadExecutor(); ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
對於執行緒池的引數需要有深入的理解後,結合實際的機器引數來進行引數設定,從而防止在使用中出現異常。
ExecutorService fixedExecutorService = new ThreadPoolExecutor( 1, 2, 60, TimeUnit.SECONDS, linkedBlockingQueue, new MyThreadFactory(), new ThreadPoolExecutor.AbortPolicy() );
ps:使用Executors.new方式建立執行緒池的缺點:
對於FixedThreadPool 和 SingleThreadPool而言
允許的請求佇列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
對於CachedThreadPool 和 ScheduledThreadPool而言
允許的建立執行緒數量為 Integer.MAX_VALUE,可能會建立大量的執行緒,從而導致 OOM。
15、使用一些日期類的時候,推薦使用LocalDateTime來替代Calendar類,或者說使用Instant來替代掉Date類。
16、儘量避免在for迴圈裡面執行try-catch操作,可以選擇將try-catch操作放在迴圈體外部使用。
正確做法:
try { for (int i = 0; i < 100; i++) { doSomeThing(); } }catch (Exception e){ e.printStackTrace(); }
不推薦做法:
for (int i = 0; i < 100; i++) { try { doSomeThing(); } catch (Exception e) { e.printStackTrace(); } }
17、對於大段的程式碼進行try-catch操作,這是一種不負責任的行為,將穩定的程式碼也都包圍在了try-catch語句塊裡面沒能很好的分清程式碼的穩定性範圍。
通常我們稱在執行中不會出錯的程式碼塊為穩定性程式碼,可能會有異常出錯的部分為非穩定性程式碼塊,後者才是try-catch重點需要關注的物件。
18、在jdk7之後,對於流這類需要關閉連線釋放資源的物件,可以使用try-with-resource處理機制來應對。
例如下方程式碼:
File file = new File("*****"); try (FileInputStream fin = new FileInputStream(file)) { //執行相關操作 } catch (Exception e) { //異常捕獲操作 }
19、使用ArrayList的時候,如果清楚它的指定大小的話,可以儘量在初始化的時候進行大小指定,因為隨著arraylist不斷新增新的元素之後,連結串列的體積會不斷增大擴容。
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
20、對於一些簡訊,郵件,電話,下單,支付等應用場景而言,開發的時候需要設定相關的防重複功能限制,防止出現某些惡意刷單,濫刷這型別情況。
21、對於敏感詞彙發表的時候,需要考慮一些文字過濾的策略。
這一塊的功能可以考慮直接接入市面上已有的成熟的UGC監控服務,或者使用公司內部自研的ugc過濾工具,防止使用者發表惡意評論等情況出現。
22、在建立索引的時候,對於索引的命名需要遵循一定的規範:
索引型別 | 命名規則 | 案例 |
---|---|---|
主鍵索引 | pk_欄位名,pk是指primary key | pk_order_id |
唯一索引 | uk_欄位名,uk是指 unique key | uk_order_id |
普通索引 | idx_欄位名,idx是指 index | idx_order_id |
23、當我們需要儲存一段文字資訊的時候,需要先考慮儲存文字的長度。
如果文字的長度超過了5000,則不建議再選擇使用varchar型別來進行儲存,可以考慮使用text型別進行資料儲存,這個時候可以考慮單獨用一張表來進行儲存資料,並且通過一個額外的主鍵id來對應,從而避免影響其他欄位的查詢。
24、在進行資料庫命名的時候儘量保證資料庫的名稱和專案工程的名稱一致。
25、在進行表結構設計的時候,只要具有唯一性質的欄位都需要建立唯一索引。
這樣有助於後期進行查詢的時候提高查詢的效率,沒有唯一索引這一層的保障,即使在業務層加入了攔截,但是依然容易造成線上髒資料的產生。
26、在進行order by這型別sql查詢的時候,需要注意查詢索引的有序性。
關於索引的建立,可以去了解一下索引的星級評定,例如三星索引。但是個人認為索引沒有所謂的最優性,需要結合實際的業務場景來設計。
27、在MySQL中,使用count(*)會統計值為 NULL 的行,而 count(列名)不會統計此列為 NULL 值的行。
28、在進行資料庫儲存引擎選擇的時候,需要結合相關的應用場景來選擇,如果是需要應用在select操作較多的情況下,可以選擇使用MyIsAM儲存引擎,如果是對於資料的insert,update,這類修改操作較多的業務場景,則優先推薦使用innodb儲存引擎。目前普遍網際網路公司都推薦使用innodb較多。
參考
阿里巴巴Java開發手冊
內容聚合閱讀
1. SpringBoot內容聚合
2. 面試題內容聚合
3. 設計模式內容聚合
4. 排序演算法內容聚合
5. 多執行緒內容聚合
&n