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

歡迎訪問 生活随笔!

生活随笔

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

java

java微妙_编码Java时的10个微妙的最佳实践

發布時間:2023/12/3 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java微妙_编码Java时的10个微妙的最佳实践 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

java微妙

這是10條最佳實踐的列表,這些最佳實踐比您的平均Josh Bloch有效Java規則要微妙得多。 盡管Josh Bloch的列表很容易學習,并且涉及日常情況,但此處的列表包含了涉及API / SPI設計的較不常見的情況,盡管這些情況可能會產生很大的影響。

我在編寫和維護jOOQ時遇到了這些問題, jOOQ是Java中的內部DSL建模SQL。 作為內部DSL,jOOQ最大限度地挑戰了Java編譯器和泛型, 將泛型,可變參數和重載組合在一起,這是Josh Bloch可能不推薦使用的“平均API”。

讓我與您分享編碼Java時的10個微妙的最佳實踐:

1.記住C ++析構函數

還記得C ++析構函數嗎? 沒有? 然后,您可能會很幸運,因為您無需再調試任何代碼,因為在刪除對象后未釋放分配的內存,從而不會導致內存泄漏。 感謝Sun / Oracle實現垃圾回收!

但是,銷毀者對他們具有一個有趣的特征。 通常以相反的順序釋放內存是有意義的。 在使用類似析構函數的語義進行操作時,也要在Java中記住這一點:

  • 當使用@Before和@After JUnit批注時
  • 分配時,釋放JDBC資源
  • 調用超級方法時

還有各種其他用例。 這是一個具體示例,顯示了如何實現某些事件偵聽器SPI:

@Override public void beforeEvent(EventContext e) {super.beforeEvent(e);// Super code before my code }@Override public void afterEvent(EventContext e) {// Super code after my codesuper.afterEvent(e); }

另一個臭名昭著的餐飲哲學家問題就是一個很好的例子,說明了為什么這很重要。

餐飲哲學家。 在這里看到: http : //adit.io/posts/2013-05-11-The-Dining-Philosophers-Problem-With-Ron-Swanson.html

規則 :無論何時使用before / after,allocate / free,take / return語義實現邏輯,請考慮after / free / return操作是否應按相反的順序執行操作。

2.不要相信您早期的SPI發展判斷

向消費者提供SPI是允許他們將自定義行為注入您的庫/代碼中的簡便方法。 不過請注意,您的SPI演變判斷可能會欺騙您,使您認為您(不需要)該附加參數 。 確實, 不應及早添加任何功能。 但是一旦發布了SPI,并決定遵循語義版本控制 ,當您意識到在某些情況下可能還需要另一個參數時,您會后悔自己在SPI中添加了一個愚蠢的單參數方法:

interface EventListener {// Badvoid message(String message); }

如果還需要消息ID和消息源怎么辦? API的發展將阻止您輕松地將該參數添加到上述類型。 使用Java 8,您可以添加防御者方法來“捍衛”您不良的早期設計決策:

interface EventListener {// Baddefault void message(String message) {message(message, null, null);}// Better?void message(String message,Integer id,MessageSource source); }

注意,不幸的是,防御者方法不能定為final 。

但是,比使用數十種方法污染SPI更好的方法是,僅為此目的使用上下文對象(或參數對象) 。

interface MessageContext {String message();Integer id();MessageSource source(); }interface EventListener {// Awesome!void message(MessageContext context); }

與EventListener SPI相比,您可以更輕松地開發MessageContext API,因為實施該應用程序的用戶將更少。

規則 :無論何時指定SPI,都應考慮使用上下文/參數對象,而不要編寫帶有固定數量參數的方法。

備注 :通常也可以通過專用的MessageResult類型(可以通過構建器API構造)來傳遞結果,這是一個好主意。 這將為您的SPI增加更多的SPI演進靈活性。

3.避免返回匿名,本地或內部類

Swing程序員可能有幾個鍵盤快捷鍵可以為其數百個匿名類生成代碼。 在許多情況下,創建它們很不錯,因為您可以本地遵守接口,而無需經歷思考完整SPI子類型生命周期的“麻煩”。

但是,您不應該過于頻繁地使用匿名,局部或內部類,原因很簡單:它們保留對外部實例的引用。 并且,如果您不小心,它們會將外部實例拖到任何地方,例如,拖到本地類之外的某個范圍。 這可能是內存泄漏的主要來源,因為整個對象圖會突然以微妙的方式糾纏在一起。

規則 :每當您編寫匿名,本地或內部類時,請檢查是否可以使其成為靜態類,甚至是常規頂級類。 避免將匿名,本地或內部類實例從方法返回到外部作用域。

備注 :對于簡單對象實例化,圍繞雙花括號有一些聰明的做法:

new HashMap<String, String>() {{put("1", "a");put("2", "b"); }}

這利用了JLS§8.6中指定的 Java實例初始化程序 。 看起來不錯(也許有點奇怪),但確實是個壞主意。 原來是完全獨立的HashMap實例現在將保留對外部實例的引用,無論發生什么情況。 此外,您將創建一個額外的類供類加載器管理。

4.立即開始編寫SAM!

Java 8正在敲門。 隨Java 8一起提供lambda ,無論您是否喜歡。 不過,您的API使用者可能會喜歡它們,因此您最好確保他們可以盡可能多地使用它們。 因此,除非您的API接受簡單的“標量”類型(例如int , long , String , Date ,否則您的API應盡可能多地接受SAM。

什么是SAM? SAM是單一抽象方法[Type]。 也稱為功能接口 ,很快將使用@FunctionalInterface注釋進行注釋 。 這與規則2配合得很好,其中EventListener實際上是SAM。 最好的SAM是具有單個參數的SAM,因為它們將進一步簡化lambda的編寫。 想象寫作

listeners.add(c -> System.out.println(c.message()));

代替

listeners.add(new EventListener() {@Overridepublic void message(MessageContext c) {System.out.println(c.message()));} });

想象一下通過jOOX進行的 XML處理,它具有幾個SAM:

$(document)// Find elements with an ID.find(c -> $(c).id() != null)// Find their child elements.children(c -> $(c).tag().equals("order"))// Print all matches.each(c -> System.out.println($(c)))

規則 :與您的API使用者保持友好, 現在已經編寫SAM /功能接口。

備注 :有關Java 8 Lambda和改進的Collections API的一些有趣的博客文章可以在這里找到:

  • http://blog.informatech.cr/2013/04/10/java-optional-objects/
  • http://blog.informatech.cr/2013/03/25/java-streams-api-preview/
  • http://blog.informatech.cr/2013/03/24/java-streams-preview-vs-net-linq/
  • http://blog.informatech.cr/2013/03/11/java-infinite-streams/

5.避免從API方法返回null

我曾經寫過一兩次關于Java的NULL的博客。 我還寫了關于Java 8的Optional簡介的博客。 從學術和實踐的角度來看,這些都是有趣的話題。

雖然NULL和NullPointerExceptions可能會在Java中困擾一段時間,但是您仍然可以以不會讓用戶遇到任何問題的方式設計API。 盡可能避免從API方法返回null。 您的API使用者應能夠在適用的情況下鏈接方法:

initialise(someArgument).calculate(data).dispatch();

在以上代碼段中,所有方法均不應返回null。 實際上,通常使用null的語義(缺少值)應該是非常例外的。 在諸如jQuery (或jOOX ,其Java端口)之類的庫中,由于始終對可迭代對象進行操作 ,因此完全避免了null。 是否匹配某項與下一個方法調用無關。

由于延遲初始化,通常還會出現空值。 在許多情況下,也可以避免延遲初始化,而不會對性能產生任何重大影響。 實際上,僅應謹慎使用惰性初始化。 如果涉及大型數據結構。

規則 :盡可能避免從方法返回null。 僅對“未初始化”或“缺少”的語義使用null。

6.切勿從API方法返回空數組或列表

雖然在某些情況下從方法返回null可以,但絕對沒有用過返回null數組或null集合的用例! 讓我們考慮一下丑陋的java.io.File.list()方法。 它返回:

在此抽象路徑名表示的目錄中命名文件和目錄的字符串數組。 如果目錄為空,則數組為空。 如果此抽象路徑名不表示目錄,或者發生I / O錯誤,則返回null。

因此,處理此方法的正確方法是

File directory = // ...if (directory.isDirectory()) {String[] list = directory.list();if (list != null) {for (String file : list) {// ...}} }

空檢查真的必要嗎? 大多數I / O操作都會產生IOException,但是此操作將返回null。 Null無法保存任何指示為什么發生I / O錯誤的錯誤消息。 因此,這在三種方式上是錯誤的:

  • 空無助于發現錯誤
  • Null不允許將I / O錯誤與不是目錄的File實例區分開
  • 每個人都會忘記空值

在集合上下文中,“空缺”的概念最好通過空數組或集合來實現。 除了再次進行延遲初始化外,幾乎沒有有用的數組或集合。

規則 :數組或集合絕不能為空。

7.避免狀態,發揮作用

HTTP的優點在于它是無狀態的。 所有相關狀態都在每個請求和每個響應中傳遞。 這對于REST的命名至關重要: 代表性狀態轉移 。 當用Java完成時,這也很棒。 當方法接收有狀態參數對象時,可以根據規則2來考慮它。 如果狀態是在這樣的對象中傳遞的,而不是從外部操縱的,那么事情會變得非常簡單。 以JDBC為例。 下面的示例從存儲過程中獲取游標:

CallableStatement s =connection.prepareCall("{ ? = ... }");// Verbose manipulation of statement state: s.registerOutParameter(1, cursor); s.setString(2, "abc"); s.execute(); ResultSet rs = s.getObject(1);// Verbose manipulation of result set state: rs.next(); rs.next();

這些使JDBC成為難以處理的API。 每個對象都是難以置信的有狀態且難以操縱。 具體來說,有兩個主要問題:

  • 在多線程環境中正確處理有狀態的API非常困難
  • 由于沒有記錄狀態,因此很難使全局狀態資源可用

阿甘正傳的戲劇海報,版權所有?1994, 派拉蒙影業 。 版權所有。 可以相信上述用法滿足了所謂的合理使用

規則 :實施更多的功能樣式。 通過方法參數傳遞狀態。 操縱較少的對象狀態。

8.短路equals()

這是一個低落的果實。 在大型對象圖中,如果所有對象的equals()方法首先便宜地比較身份,則可以顯著提高性能:

@Override public boolean equals(Object other) {if (this == other) return true;// Rest of equality logic... }

請注意,其他短路檢查可能還涉及空檢查,該檢查也應該存在:

@Override public boolean equals(Object other) {if (this == other) return true;if (other == null) return false;// Rest of equality logic... }

規則 :短路所有equals()方法以獲得性能。

9.嘗試使方法默認為final

有些人對此持不同意見,因為默認情況下使事情最終完成與Java開發人員所習慣的相反。 但是,如果您完全控制所有源代碼,則默認情況下將方法設為final絕對沒有問題,因為:

  • 如果確實需要重寫方法(確實嗎?),仍然可以刪除final關鍵字
  • 您再也不會意外覆蓋任何方法

這特別適用于靜態方法,在這些方法中,“覆蓋”(實際上是陰影)幾乎沒有任何意義。 最近,我在Apache Tika中遇到了一個非常糟糕的陰影靜態方法示例。 考慮:

  • TaggedInputStream.get(InputStream)
  • TikaInputStream.get(InputStream)

TikaInputStream擴展了TaggedInputStream并使用完全不同的實現來隱藏其靜態get()方法。

與常規方法不同,靜態方法不會互相覆蓋,因為調用站點在編譯時會綁定靜態方法調用。 如果您不走運,您可能會偶然得到錯誤的方法。

規則 :如果您完全控制自己的API,請嘗試在默認情況下盡可能多地使用final方法。

10.避免方法(T…)簽名

偶爾接受一個Object...參數的“ accept-all” varargs方法沒有任何問題:

void acceptAll(Object... all);

編寫這樣的方法給Java生態系統帶來一點JavaScript的感覺。 當然,您可能希望將實際類型限制為在實際情況下更受限的類型,例如String... 而且由于您不想限制太多,您可能會認為用通用T代替Object是一個好主意:

void acceptAll(T... all);

但事實并非如此。 T總是可以推斷為Object。 實際上,您最好不要將泛型與上述方法一起使用。 更重要的是,您可能認為可以重載上述方法,但是您不能:

void acceptAll(T... all); void acceptAll(String message, T... all);

看起來您可以選擇將String消息傳遞給該方法。 但是這里的電話怎么辦?

acceptAll("Message", 123, "abc");

編譯器會推斷<? extends Serializable & Comparable<?>> 為T <? extends Serializable & Comparable<?>> ,這使調用變得模棱兩可!

因此,每當您擁有“所有人都接受”的簽名(即使它是通用的)時,您將永遠無法再次安全地重載它。 API使用者可能只是幸運地“偶然地”選擇了編譯器選擇“正確的”最具體的方法。 但是他們也可能被欺騙使用“ accept-all”方法,或者根本無法調用任何方法。

規則 :如果可以,請避免“全部接受”簽名。 如果不能,則不要重載這種方法。

結論

Java是野獸。 與其他更高級的語言不同,它已經發展到今天。 那可能是一件好事,因為在Java的發展速度下,已經有數百項警告,這些警告只能通過多年的經驗來掌握。

參考:在JAVA,SQL和JOOQ博客上,來自我們JCG合作伙伴 Lukas Eder的Java編碼Java時的10個最佳最佳實踐 。

翻譯自: https://www.javacodegeeks.com/2013/08/10-subtle-best-practices-when-coding-java.html

java微妙

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的java微妙_编码Java时的10个微妙的最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。

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