Java 中靜態方法的用例
一、概述
靜態方法在大多數面向對象的編程語言中都很常見,包括 Java。靜態方法與實例方法的區別在於它們沒有擁有它們的對象。相反,靜態方法是在類級別定義的,可以在不創建實例的情況下使用。
在本教程中,我們將了解 Java 中靜態方法的定義以及它們的局限性。然後,我們將查看使用靜態方法的常見用例,並建議何時將它們應用到我們的代碼中。
最後,我們將看到如何測試靜態方法以及如何模擬它們。
2. 靜態方法
實例方法根據對象的運行時類型進行多態解析。另一方面,靜態方法在編譯時根據定義它們的類來解析。
2.1。班級級別
Java 中的靜態方法是類定義的一部分。我們可以通過在方法中添加static
關鍵字來定義靜態方法:
private static int counter = 0;
public static int incrementCounter() {
return ++counter;
}
public static int getCounterValue() {
return counter;
}
要訪問靜態方法,我們使用類名後跟一個點和方法名:
int oldValue = StaticCounter.getCounterValue();
int newValue = StaticCounter.incrementCounter();
assertThat(newValue).isEqualTo(oldValue + 1);
我們應該注意到,這個靜態方法可以訪問StaticCounter
類的靜態狀態。靜態方法通常是無狀態的,但它們可以作為各種技術的一部分處理類級數據,包括單例模式。
儘管也可以使用對象引用靜態方法,但這種反模式經常被Sonar等工具標記為錯誤。
2.2.限制
由於靜態方法不對實例成員進行操作,因此我們應該注意一些限制:
- 靜態方法不能直接引用實例成員變量
- 靜態方法不能直接調用實例方法
- 子類不能覆蓋靜態方法
- 我們不能在靜態方法中使用關鍵字
this
和super
以上每一項都會導致編譯時錯誤。我們還應該注意,如果我們在子類中聲明一個同名的靜態方法,它不會覆蓋而是隱藏基類方法。
3.用例
現在讓我們看一下在我們的 Java 代碼中應用靜態方法有意義的常見用例。
3.1。標準行為
當我們開發具有對其輸入參數進行操作的標準行為的方法時,使用靜態方法是有意義的。
來自 Apache StringUtils
的String
操作就是一個很好的例子:
String str = StringUtils.capitalize("baeldung");
assertThat(str).isEqualTo("Baeldung");
另一個很好的例子是Collections
類,因為它包含對不同集合進行操作的常用方法:
List<String> list = Arrays.asList("1", "2", "3");
Collections.reverse(list);
assertThat(list).containsExactly("3", "2", "1");
3.2.跨實例重用
使用靜態方法的一個正當理由是當我們跨不同類的實例重用標準行為時。
例如,我們通常在域和業務類中使用 Java Collections
和 Apache StringUtils
:
由於這些函數沒有自己的狀態,也沒有綁定到我們業務邏輯的特定部分,因此將它們保存在可以共享的模塊中是有意義的。
3.3.不改變狀態
由於靜態方法不能引用實例成員變量,因此對於不需要任何對象狀態操作的方法來說,它們是一個不錯的選擇。
當我們使用靜態方法進行狀態不受管理的操作時,方法調用更實用。調用者可以直接調用該方法,而無需創建實例。
當我們通過類的所有實例共享狀態時,例如在靜態計數器的情況下,那麼在該狀態上操作的方法應該是靜態的。管理全局狀態可能是錯誤的來源,因此當實例方法直接寫入靜態字段時,Sonar 會報告一個關鍵問題。
3.4.純函數
如果函數的返回值僅取決於傳遞的輸入參數,則該函數稱為純函數。純函數從它們的參數中獲取所有數據,並從這些數據中計算出一些東西。
純函數不對任何實例或靜態變量進行操作。因此,執行純函數也應該沒有副作用。
由於靜態方法不允許覆蓋和引用實例變量,因此它們是在 Java 中實現純函數的絕佳選擇。
4. 實用程序類
由於 Java 沒有為容納一組函數預留特定的類型,因此我們經常創建一個實用程序類。實用程序類為純靜態函數提供了歸宿。我們可以將在整個項目中重用的純函數組合在一起,而不是一遍又一遍地編寫相同的邏輯。
Java 中的實用程序類是我們永遠不應該實例化的無狀態類。因此,建議將其聲明為final
,因此它不能被子類化(這不會增加價值)。另外,為了防止任何人試圖實例化它,我們可以添加一個私有構造函數:
public final class CustomStringUtils {
private CustomStringUtils() {
}
public static boolean isEmpty(CharSequence cs) {
return cs == null || cs.length() == 0;
}
}
我們應該注意,我們放在實用程序類中的所有方法都應該是static
的。
5. 測試
讓我們檢查一下如何在 Java 中對靜態方法進行單元測試和模擬。
5.1。單元測試
使用 JUnit 對設計良好的純靜態方法進行單元測試非常簡單。我們可以使用類名來調用我們的靜態方法並傳遞一些測試參數給它。
我們的被測單元將根據其輸入參數計算結果。因此,我們可以對結果進行斷言並測試不同的輸入輸出組合:
@Test
void givenNonEmptyString_whenIsEmptyMethodIsCalled_thenFalseIsReturned() {
boolean empty = CustomStringUtils.isEmpty("baeldung");
assertThat(empty).isFalse();
}
5.2.嘲笑
大多數時候,我們不需要模擬靜態方法,我們可以在測試中簡單地使用真實的函數實現。模擬靜態方法的需要通常暗示代碼設計問題。
如果必須,那麼我們可以使用 Mockito 模擬靜態函數。但是,我們需要在 pom.xml 中添加一個額外的mockito-inline
依賴項:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
現在,我們可以使用Mockito.mockStatic
模擬對靜態方法調用的調用的方法:
try (MockedStatic<StringUtils> utilities = Mockito.mockStatic(StringUtils.class)) {
utilities.when(() -> StringUtils.capitalize("karoq")).thenReturn("Karoq");
Car car1 = new Car(1, "karoq");
assertThat(car1.getModelCapitalized()).isEqualTo("Karoq");
}
六,結論
在本文中,我們**探討了在 Java 代碼中使用靜態方法的常見用例。**我們了解了 Java 中靜態方法的定義,以及它們的局限性。
此外,我們探討了在代碼中使用靜態方法何時有意義。我們看到,對於具有標準行為的純函數來說,靜態方法是一個不錯的選擇,這些函數可以跨實例重用,但不會改變它們的狀態。最後,我們研究瞭如何測試和模擬靜態方法。
與往常一樣,完整的源代碼可在 GitHub 上獲得。