我艱難學到的三個 Java 教訓
來源:Medium 學習 Java 很困難。我從錯誤中學到了教訓。現在你也可以從我的錯誤和痛苦經歷中學習,而你不一定需要擁有這些。1. Lambda 可能會帶來麻煩。
Lambda 表達式通常超過 4 行程式碼,並且比預期的要大。這會給工作記憶帶來負擔。您需要從 lambda 更改變數嗎?你不能那樣做。為什麼?如果 lambda 可以存取呼叫站點變量,則可能會出現線程問題。因此您無法更改 lambda 中的變數。但 lambda 中的快樂路徑運作得很好。運行時失敗後,您將收到以下回應:at [CLASS].lambda$null$2([CLASS].java:85)
at [CLASS]$$Lambda$64/730559617.accept(Unknown Source)
追蹤 lambda 堆疊追蹤很困難。這些名稱令人困惑且難以追蹤和調試。更多 lambda = 更多堆疊追蹤。調試 lambda 的最佳方法是什麼?使用中間結果。
map(elem -> {
int result = elem.getResult();
return result;
});
另一個好方法是使用先進的 IntelliJ 調試技術。使用 TAB 選擇要偵錯的程式碼並將其與中間結果結合。 「當我們停在包含 lambda 的行時,如果我們按 F7(步入),那麼 IntelliJ 會突出顯示需要調試的片段。我們可以使用 Tab 切換要調試的區塊,一旦我們決定,請再次按 F7。” 如何從 lambda 存取呼叫站點變數?您只能存取最終或實際最終變數。您需要圍繞呼叫位置變數建立一個包裝器。使用AtomicType或使用您自己的類型。您可以使用 lambda 變更已建立的變數包裝器。如何解決堆疊追蹤問題?使用命名函數。這樣,您可以快速找到負責的程式碼、檢查邏輯並解決問題。使用命名函數來去除神秘的堆疊追蹤。相同的 lambda 是否重複?將其放在命名函數中。您將有一個參考點。每個 lambda 都有一個產生的函數,這使得追蹤變得困難。
lambda$yourNamedFunction
lambda$0
命名函數解決不同的問題。大 lambda。命名函數分解大型 lambda,創建更小的程式碼片段,並建立可插入函數。
.map(this::namedFunc1).filter(this::namedFilter1).map(this::namedFunc2)
2. 列表的問題
您需要使用清單(Lists)。您需要一個 HashMap來儲存資料。對於角色,您將需要一個 TreeMap。這樣的例子還在繼續。您無法避免使用集合。如何列清單?您需要什麼樣的清單?它應該是不可變的還是可變的?所有這些答案都會影響程式碼的未來。提前選擇正確的列表,這樣您就不會後悔。 Arrays::asList建立一個「端對端」清單。你不能用這份清單做什麼?您無法調整它的大小。他是不可改變的。你在這裡能做什麼?指定元素、排序或其他不影響大小的操作。請小心使用Arrays::asList,因為它的大小是不可變的,但它的內容卻不是。 new ArrayList()建立一個新的「可變」清單。建立的清單支援哪些操作?就是這樣,這就是要小心的原因。使用new ArrayList()從不可變清單建立可變清單。 List::of建立一個「不可變」集合。其大小和內容在一定條件下不變。如果內容是原始數據,例如int,則列表是不可變的。看一下下面的例子。@Test
public void testListOfBuilders() {
System.out.println("### TESTING listOF with mutable content ###");
StringBuilder one = new StringBuilder();
one.append("a");
StringBuilder two = new StringBuilder();
two.append("a");
List<StringBuilder> asList = List.of(one, two);
asList.get(0).append("123");
System.out.println(asList.get(0).toString());
}
### 使用可變內容測試 listOF ### a123
您需要建立不可變物件並將它們插入到List::of中。但是,List::of不提供任何不變性保證。 List::of提供不變性、可靠性和可讀性。知道何時使用可變結構以及何時使用不可變結構。不應變更的參數清單應位於不可變清單中。可變列表可以是可變列表。了解創建可靠程式碼所需的集合。
3.註解會減慢你的速度
你使用註解嗎?你了解他們嗎?你知道他們在做什麼嗎?如果您認為Logged註解適用於每種方法,那麼您就錯了。我使用Logged來記錄方法參數。令我驚訝的是,它不起作用。@Transaction
@Method("GET")
@PathElement("time")
@PathElement("date")
@Autowired
@Secure("ROLE_ADMIN")
public void manage(@Qualifier('time')int time) {
...
}
這段程式碼有什麼問題?這裡有很多配置摘要。你會多次遇到這種情況。配置與常規代碼混合。本身並不壞,但它吸引了你的眼球。需要註解來減少樣板程式碼。您不需要為每個端點編寫日誌記錄邏輯。無需設定事務,使用@Transactional。註釋透過提取程式碼來減少模式。這裡沒有明顯的贏家,因為兩者都在遊戲中。我仍然使用 XML 和註釋。當您發現重複模式時,最好將邏輯移至註解中。例如,日誌記錄是一個很好的註解選項。寓意:不要過度使用註釋,也不要忘記 XML。
獎勵:您可能會遇到可選問題
您將使用Optional中的orElse。當您不傳遞orElse常數時,會發生不良行為。您應該意識到這一點,以防止將來出現問題。讓我們來看幾個例子。當getValue(x)傳回值時,執行getValue(y)。如果getValue(x)傳回非空可選值,則執行orElse中的方法。getValue(x).orElse(getValue(y)
.orElseThrow(() -> new NotFoundException("value not present")));
public Optional<Value> getValue(Source s)
{
System.out.println("Source: " + s.getName());
// returns value from s source
}
// when getValue(x) is present system will output
Source: x
Source: y
使用orElseGet。它不會執行非空Options的程式碼。
getValue(x).orElseGet(() -> getValue(y)
.orElseThrow(() -> new NotFoundException("value not present")));
public Optional<Value> getValue(Source s)
{
System.out.println("Source: " + s.getName());
// returns value from s source
}
// when getValue(x) is present system will output
Source: x
結論
學習Java很難。你不可能在 24 小時內學會 Java。磨練你的技能。花時間學習並在工作中表現出色。如何在程式碼中使用 SOLID 原則
來源:Cleanthecode 編寫可靠的程式碼需要堅實的原則。在某些時候,我們都必須學習如何程式設計。老實說。我們很愚蠢。我們的程式碼是相同的。感謝上帝,我們有固體。堅實的原則
那麼如何寫 SOLID 程式碼呢?其實很簡單。您只需要遵循以下五個規則:- 單一責任原則
- 開閉原則
- 里氏替換原理
- 介面分離原理
- 依賴倒置原則
單一責任原則
羅伯特·C·馬丁 (Robert C. Martin) 在他的書中這樣描述這一原則:“一個類別應該只有一個改變的理由。” 我們一起來看兩個例子。1. 不該做什麼
我們有一個名為User的類,它允許使用者執行以下操作:- 註冊帳戶
- 登入
- 首次登入時收到通知
2. 做什麼
想像一個應該向新使用者顯示通知的類別FirstUseNotification。它將由三個功能組成:- 檢查通知是否已經顯示
- 顯示通知
- 將通知標記為已顯示
開閉原則
開閉原則是 Bertrand Meyer 提出的:“軟體物件(類別、模組、函數等)應該對擴展開放,但對修改關閉。” 這個原理其實很簡單。您必須編寫程式碼,以便可以在不更改原始程式碼的情況下向其中添加新功能。這有助於防止出現需要更改依賴已修改類別的類別的情況。然而,這項原則的實施卻困難得多。邁耶建議使用繼承。但這會帶來牢固的聯繫。我們將在介面分離原則和依賴倒置原則中討論這一點。於是Martin想出了一個更好的方法:使用多態性。此方法不使用傳統的繼承,而是使用抽象基底類別。這樣,繼承規範可以被重複使用,而無需實作。介面可以編寫一次,然後關閉以進行更改。新函數必須實作該介面並擴展它。里氏替換原理
這項原則是由圖靈獎得主芭芭拉·利斯科夫(Barbara Liskov)發明的,她因對程式語言和軟體方法論的貢獻而獲得了圖靈獎。在她的文章中,她將自己的原則定義如下:“程式中的物件應該可以用其子類型的實例替換,而不影響程式的正確執行。” 讓我們以程式設計師的身份來看看這個原則。想像我們有一個正方形。它可能是一個矩形,這聽起來很合乎邏輯,因為正方形是矩形的特殊形狀。這就是里氏替換原理可以發揮作用的地方。無論您希望在程式碼中的何處看到矩形,也有可能出現正方形。現在假設您的矩形有SetWidth和SetHeight方法。這意味著廣場也需要這些手段。不幸的是,這沒有任何意義。這意味著這裡違反了里氏替換原則。介面分離原理
與所有原則一樣,介面分離的原則比看起來簡單得多:“許多特定於客戶端的介面比一個通用介面更好。” 與單一責任原則一樣,目標是減少副作用和所需的更改數量。當然,沒有人故意寫這樣的程式碼。但很容易遇到。還記得前面原理中的平方嗎?現在想像我們決定實施我們的計劃:我們從一個矩形生成一個正方形。現在我們強迫正方形實作setWidth和setHeight,這可能不會做任何事情。如果他們這樣做,我們可能會破壞一些東西,因為寬度和高度不是我們所期望的。對我們來說幸運的是,這意味著我們不再違反里氏替換原則,因為我們現在允許在任何使用矩形的地方使用正方形。然而,這產生了一個新問題:我們現在違反了分離介面的原則。我們強制衍生類別實作它選擇不使用的功能。依賴倒置原則
最後一個原則很簡單:高層模組應該是可重用的,並且不應該受到低層模組變更的影響。- A. 高層模組不應該依賴低層模組。兩者都必須依賴抽象(例如介面)。
- B. 抽像不應依賴細節。細節(具體實作)必須取決於抽象。
- 高層模組,取決於抽象
- 依賴相同抽象的低階模組
GO TO FULL VERSION