日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Effective Java 在工作中的应用总结

發布時間:2024/8/23 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Effective Java 在工作中的应用总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介:?《Effective Java》是一本經典的 Java 學習寶典,值得每位 Java 開發者閱讀。筆者將書中和平日工作較密切的知識點做了部分總結。

作者 | 宜秋
來源 | 阿里技術公眾號

《Effective Java》是一本經典的 Java 學習寶典,值得每位 Java 開發者閱讀。筆者將書中和平日工作較密切的知識點做了部分總結。

一 創建和銷毀對象篇

1 若有多個構造器參數時,優先考慮構造器

當類構造包含多個參數時,同學們會選擇 JavaBeans 模式。在這種模式下,可以調用一個無參構造器來創建對象,然后調用 setter 方法來設置必要和可選的參數。目前較受歡迎的方法之一如在類上加入 Lombok 提供的@Data注解,來自動生成getter/setter、equals 等方法。但是JavaBeans模式無法將類做成不可變(immutable,詳見“使可變形最小化”章節)。這就需要開發者自己掌控值的更新情況,確保線程安全等。

推薦:Builder模式

Builder 模式通過 builder 對象上,調用類似 setter 的方法,設置相關的參數(類似 Proto Buffers)。最后,通過調用 build 方法來生成不可變的對象(immutable object)。使用 Builder 模式的方法之一包括在類上加入 Lombok 提供的 @Builder 注解。

應用:API Request & Response

在微服務架構中,服務的請求(request)和響應(response)往往包含較多參數。在處理請求的過程中,筆者也常常會擔心誤操作修改了請求的內容。所以,筆者傾向使用Builder模式。

我們可使用Builder模式來構建該類型對象。在構建過程中,若需要引入額外邏輯(e.g. if-else),可先返回Builder對象,最后再調用build方法。

import lombok.Builder;/** 請求類 */ @Builder public class SampleRequest {private String paramOne;private int paramTwo;private boolean paramThree; }/** 響應類 */ @Builder public class SampleResponse {private boolean success; }/** 服務接口 */ public interface SampleFacade {Result< SampleResponse> rpcOne(RequestParam< SampleRequest>); }/** 調用 */ public void testRpcOne() {SampleRequest request =SampleRequest.builder().paramOne("one").paramTwo(2).paramThree(true).build();Result< SampleResponse> response = sampleFacade.rpcOne(request); }

2 通過私有構造器強化不可實例化的能力

有些類,例如工具類(utility class),只包含靜態字段和靜態方法。這些類應盡量確保不被實例化,防止用戶誤用。

推薦:私有化類構造器

為了防止誤導用戶,認為該類是專門為了繼承而設計的,我們可以將構造器私有化。

public class SampleUtility {public static String getXXX() {return "test";} /** 私有化構造器 */private SampleUtility() {} }/** 直接調用方法 */ public static void main(String[] args) {System.out.println(SampleUtility.getXXX()); }

二 類和接口篇

1 最小化類和成員的可訪問性

盡可能地使每個類或者成員不被外界訪問。

推薦:有的時候,為了測試,我們不得不將某些私有的(private)類、接口或者成員變成包級私有的(package-private)。這里,筆者推薦大家使用 Guava 提供的 @VisiableForTesting 注解,來提示這是為了測試而使可訪問級別變為包級私有,放寬了限制。

import com.google.common.annotations.VisibleForTesting;@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) String getXXX() {return "test"; }

此外,也有小伙伴推薦 PowerMock 單元測試框架。PowerMock 是 Mockito 的加強版,可以實現完成對private/static/final方法的Mock(模擬)。通過加入 @PrepareForTest 注解來實現。

public class Utility {private static boolean isGreaterThan(int a, int b) {return a > b;}private Utility() {} }/** 測試類 */ import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox;@RunWith(PowerMockRunner.class) @PrepareForTest({Utility.class}) public class UtilityTest {@Testpublic void test_privateIsGreaterThan_success() throws Exception {/** 測試私有的 isGreaterThan 方法 */boolean result = Whitebox.invokeMethod(Utility.class, "isGreaterThan", 3, 2);Assertions.assertTrue(result);} }

2 使可變形最小化

不可變類(immutable class)是指類對應的實例被創建后,就無法改變其成員變量值。即實例中包含的所有信息都必須在創建該實例的時候提供,并在對象的生命周期內固定不變。

不可變類一般采用函數(functional)模式,即對應的方法返回一個函數的結果,函數對操作數進行運算但并不修改它。與之相對應的更常見的是過程的(procedure)或者命令式的(imperative)做法。使用這些方法時,將一個過程作用在它們的操作數上,會導致它的狀態發生改變。

如在“若有多個構造器參數時,優先考慮構造器”一節中提到,不可變對象比較簡單,線程安全,只有一種狀態。使用該類的開發者無需再做額外的工作來維護約束關系。另外,可變的對象可以有任意復雜的狀態。若 mutator 方法(e.g. update)無詳細的描述,開發者需要自行閱讀方法內容。筆者經常會花費較多時間弄清楚在某方法內,可變對象的哪些字段被更改,方法結束后會不會影響后續的對象操作。筆者推薦傳入不可變對象,基于此用更新的參數創建新的不可變對象返回。雖然會創建更多的對象,但是保證了不可變形,以及更可讀性。

推薦:Guava Collection之Immutable類

筆者在日常開發中傾向將 Immutable 類(ImmutableList,ImmutableSet,ImmuableMap)和上文提到的函數模式集合,實現mutator 類方法。

import static com.google.common.collect.ImmutableList.toImmutableList; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap;/** 推薦 */ private static final ImmutableMap< String, Integer> SAMPLE_MAP =ImmutableMap.of("One", 1, "Two", 2);/** 推薦:確保原input列表不會變化 */ public ImmutableList< TestObj> updateXXX(ImmutableList< TestObj> input) {return input.stream().map(obj -> obj.setXXX(true)).collect(toImmutableList()); }/** 不推薦:改變input的信息 */ public void filterXXX(List< TestObj> input) {input.forEach(obj -> obj.setXXX(true)); }

三 泛型篇

1 列表優先于數組

數組是協變的(covariant),即Sub為Super的子類型,那么數組類型Sub[] 就是Super[] 的子類型;數組是具體化的,在運行時才知道并檢查它們的元素類型約束。而泛型是不可變的和可擦除的(即編譯時強化它們的類型信息,并在運行時丟棄)。

需要警惕 public static final 數組的出現。很有可能是個安全漏洞!

四 方法篇

1 校驗參數的有效性

若傳遞無效的參數值給方法,這個方法在執行復雜、耗時邏輯之前先對參數進行了校驗(validation),便很快就會失敗,并且可清楚地拋出適當的異常。若沒有校驗它的參數,就可能會在后續發生各種奇怪的異常,有時難以排查定位原因。

筆者認為,微服務提供的API request 也應沿用這一思想。即在API 請求被服務處理之前,先進行參數校驗。每個request應與對應的request validator 綁定。若參數值無效,則拋出特定的ClientException(e.g. IllegalArgumentException)。

2 謹慎設計方法簽名

  • 謹慎地選擇方法的名稱:

    • 執行某個動作的方法通常用動詞或者動詞短語命名:createXXX,updateXXX,removeXXX,convertXXX,generateXXX
    • 對于返回boolean值的方法,一般以 is 開頭:isValid,isLive,isEnabled
  • 避免過長的參數列表:目標是四個參數,或者更少。

    • 當參數過多時,筆者會使用Pair,Triple或輔助類(e.g. 靜態成員類)
public class SampleListener {public ConsumeConcurrentlyStatus consumeMessage(String input) {SampleResult result = generateResult(input);...} private static SampleResult generateResult(String input) {...}/** 輔助類 */private static class SampleResult {private boolean success;private List< String> xxxList;private int count;} }

3 返回零長度的數組或者集合,而不是null

若一個方法返回 null 而不是零長度的數組或者集合,開發者需要加入 != null 的檢查,有時容易忘記出錯,報NullpointerException。

說到此,筆者想額外提一下 Optional。網絡上有很多關于 Optional 和 null 的使用討論。Optional 允許調用者繼續一系列流暢的方法調用(e.g. stream.getFirst().orElseThrow(() -> new MyFancyException()))。以下為筆者整理的觀點。

/** 推薦:提示返回值可能為空。*/ public Optional< Foo> findFoo(String id);/*** 中立:稍顯笨重* 可考慮 doSomething("bar", null);* 或者重載 doSomething("bar"); 和 doSomething("bar", "baz");**/ public Foo doSomething(String id, Optional< Bar> barOptional);/** * 不推薦:違背 Optional 設計的目的。* 當 Optional 值缺省時,一般有3種處理方法:1)提供代替的值;2)調用方法提供代替的值;3)拋出異常* 這些處理方法可以在字段初始或賦值的時候處理。**/ public class Book {private List< Pages> pages;private Optional< Index> index; }/** * 不推薦:違背 Optional 設計的目的。* 若為缺省值,可直接不放入列表中。**/ List< Optional< Foo>>

五 通用程序設計篇

1 如果需要精確的答案,請避免使用float和double

float 和 double 類型主要用于科學工程計算。它們執行二進制浮點運算,為了在數值范圍上提供較為精準的快速近似計算。但是,它們并不能提供完全精確的結果,尤其不適合用于貨幣計算。float 或者 double 精確地表示0.1 是不可行的。

若需系統來記錄十進制小數點,可使用BigDecimal。

2 基本類型優先于裝箱基本類型

基本類型(primitive)例如 int、double、long 和 boolean。每個基本類型都有一個對應的引用類型,稱作裝箱基本類型(boxed primitive),對應為Integer、Double、Long 和 Boolean。如書中提到,它們的區別如下:

/** 推薦 */ public int sum(int a, int b) {return a + b; }/** 不推薦:不必要的裝箱 */ public Integer sum(Integer a, Integer b) {return a + b; }

若無特殊的使用場景,推薦總是使用基本類型。若不得不使用裝箱基本類型,注意 == 操作和 NullPointerException 異常。裝箱基本類型的使用場景:

  • 作為集合中的元素(e.g. Set< Long>)
  • 參數化類型(e.g. ThreadLocal< Long>)
  • 反射的方法調用

六 異常

1 每個方法拋出的異常都要有文檔

始終要單獨地聲明受檢的異常,并且利用Javadoc的@throws標記,準確地記錄下拋出每個異常的條件。

在日常工作中,筆者調用其他組的 API 時,有時會發現一些意料之外的異常。良好的文檔記錄,可以幫助 API 調用者更好得處理相關的異常。文檔記錄可包括:異常的類型,異常的 error code,和描述。

2 其他

一些公司將 API 產生的異常分成 ClientException 和 ServerException。一般 ClientException (e.g. 無效的服務 request ) 是由調用方非常規調用 API 導致的異常處理,可不在服務端主要的異常監測范圍中。而 ServerException(e.g. 數據庫查詢超時)是由服務端自身原因導致的問題,平時需要著重監測。

七 引用

Bloch, Joshua. 2018. Effective Java, 3rd Edition

原文鏈接
本文為阿里云原創內容,未經允許不得轉載。

總結

以上是生活随笔為你收集整理的Effective Java 在工作中的应用总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。