簡體   English   中英

什么時候不要在 Java 中使用 static 關鍵字?

[英]When NOT to use the static keyword in Java?

什么時候在方法簽名上使用 Java 中的 static 關鍵字被認為是不好的做法? If a method performs a function based upon some arguments, and does not require access to fields that are not static, then wouldn't you always want these types of methods to be static?

您將在大型Java應用程序中遇到的兩個最大的弊端是

  • 靜態方法,除了那些純函數*
  • 可變靜態字段

這些破壞了代碼的模塊性,可擴展性和可測試性,我意識到我無法在這個有限的時間和空間中說服你。

*“純函數”是任何不修改任何狀態的方法,其結果僅取決於提供給它的參數。 因此,例如,任何執行I / O(直接或間接)的函數都不是純函數,但Math.sqrt()當然是。

關於純粹功能 (自我鏈接)的更多blahblah以及為什么要堅持它們。

我強烈建議您支持“依賴注入”編程風格,可能由Spring或Guice等框架支持(免責聲明:我是后者的合着者)。 如果你這樣做的權利,你將永遠不會實質上需要可變靜止狀態還是非純靜態方法。

可能不希望它是靜態的一個原因是允許它在子類中被覆蓋。 換句話說,行為可能不依賴於對象內的數據,而是取決於對象的確切類型。 例如,你可能有一個通用的集合類型,與isReadOnly財產將返回false的永遠可變集合, true的永遠不變的集合,而在其他依賴於實例變量。

但是,根據我的經驗,這種情況非常罕見 - 通常應明確說明。 通常我會創建一個不依賴於任何對象狀態靜態的方法。

一般來說,我更喜歡實例方法,原因如下:

  1. 靜態方法使測試變得困難,因為它們無法替換,
  2. 靜態方法更加面向程序。

在我看來,靜態方法對於實用程序類(如StringUtils )是可以的,但我更願意盡量避免使用它們。

你說的是真的,但是當你想要在派生類中覆蓋該方法的行為時會發生什么? 如果它是靜態的,你不能這樣做。

作為示例,請考慮以下DAO類型類:

class CustomerDAO {
    public void CreateCustomer( Connection dbConn, Customer c ) {
       // Some implementation, created a prepared statement, inserts the customer record.
    }

    public Customer GetCustomerByID( Connection dbConn, int customerId ) {
       // Implementation
    }
}

現在,這些方法都不需要任何“狀態”。 他們需要的一切都作為參數傳遞。 所以他們很容易變得靜止。 現在要求你需要支持一個不同的數據庫(比如說Oracle)

由於這些方法不是靜態的,您可以創建一個新的DAO類:

class OracleCustomerDAO : CustomerDAO {
    public void CreateCustomer( Connection dbConn, Customer c ) {
        // Oracle specific implementation here.
    }

    public Customer GetCustomerByID( Connection dbConn, int customerId ) {
        // Oracle specific implementation here.
    }
}

現在可以使用這個新類來代替舊類。 如果您正在使用依賴注入,它甚至可能根本不需要更改代碼。

但是如果我們將這些方法設置為靜態,那么事情會變得更復雜,因為我們不能簡單地覆蓋新類中的靜態方法。

靜態方法通常用於兩個目的。 第一個目的是使用某種全局實用方法,類似於java.util.Collections中的功能。 這些靜態方法通常是無害的。 第二個目的是通過各種設計模式(如單例工廠 )來控制對象實例化並限制對資源(例如數據庫連接)的訪問。 如果實施不當,這些可能會導致問題。

對我來說,使用靜態方法有兩個缺點:

  1. 它們使代碼更少模塊化,更難以測試/擴展。 大多數答案已經解決了這個問題,所以我不再討論了。
  2. 靜態方法往往會導致某種形式的全局狀態,這通常是潛在的錯誤的原因。 這可能發生在為上述第二個目的而編寫的編寫不良的代碼中。 讓我詳細說明一下。

例如,考慮一個需要將某些事件記錄到數據庫的項目,並依賴於其他狀態的數據庫連接。 假設通常首先初始化數據庫連接,然后將日志記錄框架配置為將某些日志事件寫入數據庫。 現在假設開發人員決定從手寫的數據庫框架轉移到現有的數據庫框架,例如hibernate。

但是,這個框架很可能有自己的日志記錄配置 - 如果它恰好使用與您相同的日志框架,那么配置之間很可能會出現各種沖突。 突然之間,切換到不同的數據庫框架會導致系統中看似無關的不同部分的錯誤和故障。 這種故障可能發生的原因是因為日志記錄配置維護通過靜態方法和變量訪問的全局狀態,並且系統的不同部分可以覆蓋各種配置屬性。

為了擺脫這些問題,開發人員應該避免通過靜態方法和變量存儲任何狀態。 相反,他們應該構建干凈的API,讓用戶根據需要管理和隔離狀態。 BerkeleyDB就是一個很好的例子,通過Environment對象而不是靜態調用封裝狀態。

那就對了。 實際上,您必須將可能是合理設計(將一些與類無關的函數)轉換為Java術語。 這就是為什么你會看到像FredsSwingUtils和YetAnotherIOUtils這樣的全能類。

如果要獨立於該類的任何對象使用類成員,則應將其聲明為static。
如果它被聲明為static,則可以在沒有該類對象的現有實例的情況下訪問它。 靜態成員由該特定類的所有對象共享。

關於靜態方法的另一個煩惱是:沒有簡單的方法來傳遞對這樣的函數的引用而不在它周圍創建包裝類。 例如 - 類似於:

FunctorInterface f = new FunctorInterface() { public int calc( int x) { return MyClass.calc( x); } };

我討厭這種java make-work。 也許更高版本的java會獲得委托或類似的函數指針/程序類型機制?

一個輕微的抱怨,但還有一件事喜歡無償的靜態功能,呃,方法。

這里有兩個問題1)創建對象的靜態方法在第一次訪問時會保留在內存中嗎? 這個(在內存中仍然加載)是一個缺點嗎? 2)使用Java的一個優點是它的垃圾收集功能 - 當我們使用靜態方法時,我們是否忽略了這一點?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM