亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

10個微妙的Java編碼最佳實踐

2024-07-13 10:13:26
字體:
來源:轉載
供稿:網友

這是一個比Josh Bloch的Effective Java規則更精妙的10條Java編碼實踐的列表。和Josh Bloch的列表容易學習并且關注日常情況相比,這個列表將包含涉及API/SPI設計中不常見的情況,可能有很大影響。

我在編寫和維護jOOQ(Java中內部DSL建模的SQL)時遇到過這些。作為一個內部DSL,jOOQ最大限度的挑戰了Java的編譯器和泛型,把泛型,可變參數和重載結合在一起,Josh Bloch可能不會推薦的這種太寬泛的API。

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

1. 牢記C++的析構函數

記得C++的析構函數?不記得了?那么你真的很幸運,因為你不必去調試那些由于對象刪除后分配的內存沒有被釋放而導致內存泄露的代碼。感謝Sun/Oracle實現的垃圾回收機制吧!

盡管如此,析構函數仍提供了一個有趣的特征。它理解逆分配順序釋放內存。記住在Java中也是這樣的,當你操作類析構函數語法:

  • 使用JUnit的@Before和@After注釋
  • 分配,釋放JDBC資源
  • 調用super方法

還有其他各種用例。這里有一個具體的例子,說明如何實現一些事件偵聽器的SPI:

@Overridepublic void beforeEvent(EventContext e) { super.beforeEvent(e); // Super code before my code}@Overridepublic void afterEvent(EventContext e) { // Super code after my code super.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 { // Bad void message(String message);}

如果你也需要消息ID和消息源,怎么辦?API演進將會阻止你向上面的類型添加參數。當然,有了Java8,你可以添加一個defender方法,“防御”你早期糟糕的設計決策:

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

注意,不幸的是,defender方法不能使用final修飾符。
但是比起使用許多方法污染你的SPI,使用上下文對象(或者參數對象)會好很多。

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

比起EventListner SPI你可以更容易演進MessageContext API,因為很少用戶會實現它。
規則: 無論何時指定SPI時,考慮使用上下文/參數對象,而不是寫帶有固定參數的方法。
備注: 通過專用的MessageResult類型交換結果也是一個好主意,該類型可以使用建設者API構造它。這樣將大大增加SPI進化的靈活性。

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

Swing程序員通常只要按幾下快捷鍵即可生成成百上千的匿名類。在多數情況下,只要遵循接口、不違反SPI子類型的生命周期(SPI subtype lifecycle),這樣做也無妨。 但是不要因為一個簡單的原因——它們會保存對外部類的引用,就頻繁的使用匿名、局部或者內部類。因為無論它們走到哪,外部類就得跟到哪。例如,在局部類的域外操作不當的話,那么整個對象圖就會發生微妙的變化從而可能引起內存泄露。

規則:在編寫匿名、局部或內部類前請三思能否將它轉化為靜態的或普通的頂級類,從而避免方法將它們的對象返回到更外層的域中。

注意:使用雙層花括號來初始化簡單對象:

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

這個方法利用了 JLS §8.6規范里描述的實例初始化方法(initializer)。表面上看起來不錯,但實際上不提倡這種做法。因為要是使用完全獨立的HashMap對象,那么實例就不會一直保存著外部對象的引用。此外,這也會讓類加載器管理更多的類。

4. 現在就開始編寫SAM!

Java8的腳步近了。伴隨著Java8帶來了lambda表達式,無論你是否喜歡。盡管你的API用戶可能會喜歡,但是你最好確保他們可以盡可能經常的使用。因此除非你的API接收簡單的“標量”類型,比如int、long、String 、Date,否則讓你的API盡可能經常的接收SAM。

什么是SAM?SAM是單一抽象方法[類型]。也稱為函數接口,不久會被注釋為@FunctionalInterface。這與規則2很配,EventListener實際上就是一個SAM。最好的SAM只有一個參數,因為這將會進一步簡化lambda表達式的編寫。設想編寫

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

來替代

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

設想以JOOX的方式來處理XML。JOOX就包含很多的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/函數接口。

備注:有許多關于Java8 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.避免讓方法返回null

我曾寫過1、2篇關于java NULLs的文章,也講解過Java8中引入新的Optional類。從學術或實用的角度來看,這些話題還是比較有趣的。

盡管現階段Null和NullPointerException依然是Java的硬傷,但是你仍可以設計出不會出現任何問題的API。在設計API時,應當盡可能的避免讓方法返回null,因為你的用戶可能會鏈式調用方法:

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

從上面代碼中可看出,任何一個方法都不應返回null。實際上,在通常情況下使用null會被認為相當的異類。像 jQuery或 jOOX這樣的庫在可迭代的對象上已完全的摒棄了null。

Null通常用在延遲初始化中。在許多情況下,在不嚴重影響性能的條件下,延遲初始化也應該被避免。實際上,如果涉及的數據結構過于龐大,那么就要慎用延遲初始化。

規則:無論何時方法都應避免返回null。null僅用來表示“未初始化”或“不存在”的語義。

6.設計API時永遠不要返回空(null)數組或List

盡管在一些情況下方法返回值為null是可以的,但是絕不要返回空數組或空集合!請看 java.io.File.list()方法,它是這樣設計的:

此方法會返回一個指定目錄下所有文件或目錄的字符串數組。如果目錄為空(empty)那么返回的數組也為空(empty)。如果指定的路徑不存在或發生I/O錯誤,則返回null。

因此,這個方法通常要這樣使用:

File directory = // ...if (directory.isDirectory()) { String[] list = directory.list(); if (list != null) { for (String file : list) {  // ... } }}
大家覺得null檢查有必要嗎?大多數I/O操作會產生IOExceptions,但這個方法卻只返回了null。Null是無法存放I/O錯誤信息的。因此這樣的設計,有以下3方面的不足:
  • Null無助于發現錯誤
  • Null無法表明I/O錯誤是由File實例所對應的路徑不正確引起的
  • 每個人都可能會忘記判斷null情況

以集合的思維來看待問題的話,那么空的(empty)的數組或集合就是對“不存在”的最佳實現。返回空(null)數組或集合幾乎是無任何實際意義的,除非用于延遲初始化。

規則:返回的數組或集合不應為null。

7. 避免狀態,使用函數

HTTP的好處是無狀態。所有相關的狀態在每次請求和響應中轉移。這是REST命名的本質:含狀態傳輸(Representational state transfer)。在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
  • 很難讓有狀態的資源全局可用,因為狀態沒有被描述

規則:更多的以函數風格實現。通過方法參數轉移狀態。極少操作對象狀態。

8. 短路式 equals()

這是一個比較容易操作的方法。在比較復雜的對象系統中,你可以獲得顯著的性能提升,只要你在所有對象的equals()方法中首先進行相等判斷:

@Overridepublic boolean equals(Object other) { if (this == other) return true; // 其它相等判斷邏輯...}

注意,其它短路式檢查可能涉及到null值檢查,所以也應當加進去:

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

規則: 在你所有的equals()方法中使用短路來提升性能。

9. 盡量使方法默認為final

有些人可能不同意這一條,因為使方法默認為final與Java開發者的習慣相違背。但是如果你對代碼有完全的掌控,那么使方法默認為final是肯定沒錯的:

  • 如果你確實需要覆蓋(override)一個方法(你真的需要?),你仍然可以移除final關鍵字
  • 你將永遠不會意外地覆蓋(override)任何方法

這特別適用于靜態方法,在這種情況下“覆蓋”(實際上是遮蔽)幾乎不起作用。我最近在Apache Tika中遇到了一個很糟糕的遮蔽靜態方法的例子??匆幌拢?/p>

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

TikaInputStream擴展了TaggedInputStream,以一種相對不同的實現遮蔽了它的靜態get()方法。

與常規方法不同,靜態方法不能互相覆蓋,因為調用的地方在編譯時就綁定了靜態方法調用。如果你不走運,你可能會意外獲得錯誤的方法。

規則:如果你完全掌控你的API,那么使盡可能多的方法默認為final。

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

在特殊場合下使用“accept-all”變量參數方法接收一個Object…參數就沒有錯的:

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");

編譯器將T推斷為<? extends Serializable & Comparable<?>>,這將會使調用不明確!

所以無論何時你有一個“accept-all”簽名(即使是泛型),你將永遠不能類型安全地重載它。API使用者可能僅僅在走運的時候才會讓編譯器“偶然地”選擇“正確的”方法。但是也可能使用accept-all方法或者無法調用任何方法。

規則: 如果可能,避免“accept-all”簽名。如果不能,不要重載這樣的方法。

結論

Java是一個野獸。不像其它更理想主義的語言,它慢慢地演進為今天的樣子。這可能是一件好事,因為以Java的開發速度就已經有成百上千個警告,而且這些警告只能通過多年的經驗去把握。

敬請期待更多關于這個主題的前十名列表!

原文鏈接: jooq 翻譯: ImportNew.com - liken
譯文鏈接: http://www.importnew.com/10138.html
[ 轉載請保留原文出處、譯者和譯文鏈接。]

 

注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
午夜精品久久久久久久男人的天堂| 日韩精品在线视频观看| 亚洲美女动态图120秒| 少妇高潮久久久久久潘金莲| 国产精品成人免费视频| 亚洲自拍偷拍视频| 中文字幕欧美精品日韩中文字幕| 一本一道久久a久久精品逆3p| 亚洲精品美女久久久| 中文字幕国产精品| 国内精品久久影院| 日韩最新免费不卡| 国产成人av网| 久久久99免费视频| 日韩电影中文字幕一区| 国产成人精品综合久久久| 亚洲影视中文字幕| 九九久久国产精品| 91精品久久久久久久| 欧美日韩国产一区在线| 日韩精品久久久久久久玫瑰园| 伊人精品在线观看| 尤物tv国产一区| 欧美成人精品一区| 国产精品一区二区av影院萌芽| 亚洲国产天堂久久综合| 欧美一级高清免费播放| 亚洲最大成人网色| 日韩av在线影视| 51精品在线观看| 国产不卡一区二区在线播放| 国产99久久久欧美黑人| 一夜七次郎国产精品亚洲| 91精品视频一区| 欧洲美女7788成人免费视频| 在线电影av不卡网址| 欧美精品videofree1080p| 亚洲国产日韩欧美在线图片| 激情久久av一区av二区av三区| 爽爽爽爽爽爽爽成人免费观看| 国产精品美乳一区二区免费| 免费不卡欧美自拍视频| 久久精品人人爽| 98精品国产自产在线观看| 亚洲午夜精品视频| 91在线免费网站| yw.139尤物在线精品视频| 国产午夜精品久久久| 日韩精品久久久久| 国产精品久久久久久久久免费| 国产美女高潮久久白浆| 欧美成aaa人片免费看| 91av在线国产| 色777狠狠综合秋免鲁丝| 精品国模在线视频| 国产综合福利在线| 欧美精品免费播放| 日韩精品免费在线播放| 久久国内精品一国内精品| 欧美色播在线播放| 国产精品成人免费电影| 神马国产精品影院av| 亚洲男人天堂视频| 久久久中精品2020中文| 亚洲成人网久久久| 国内精久久久久久久久久人| 日韩欧美精品免费在线| 亚洲美女免费精品视频在线观看| 欧美激情影音先锋| 91免费在线视频| 亚洲成人在线网| 2021国产精品视频| 亚洲韩国日本中文字幕| 亚洲色图35p| 欧美一区二区三区免费视| 久久中文字幕视频| 精品动漫一区二区三区| 欧美亚洲国产另类| 欧美视频二区36p| 欧美成人精品h版在线观看| 丝袜情趣国产精品| 久久久久久有精品国产| 久久久久久久国产精品| 狠狠躁夜夜躁人人爽超碰91| 丝袜一区二区三区| 欧美裸体xxxxx| 国产欧美一区二区三区久久| 97在线精品国自产拍中文| 国产精品日韩在线| 国产精品欧美激情| 91国产中文字幕| 日韩中文字幕在线看| 在线精品91av| 亚洲欧洲xxxx| 日本久久91av| 97碰在线观看| 中文字幕在线国产精品| 91高潮精品免费porn| 中文一区二区视频| 超碰日本道色综合久久综合| 97免费中文视频在线观看| 日韩有码在线电影| 亚洲天堂av女优| 精品福利一区二区| 欧美亚洲激情视频| 国产精品mp4| 成人啪啪免费看| 免费99精品国产自在在线| 亚洲自拍欧美另类| 亚洲va欧美va国产综合剧情| 国产精品久久久久久中文字| 美女啪啪无遮挡免费久久网站| 国产精品高清在线| 久久99热这里只有精品国产| 亚洲精品小视频| 久热在线中文字幕色999舞| 欧美在线视频免费播放| 亚洲国产精品yw在线观看| 国产成人在线播放| 国产精品第2页| 黄色成人在线免费| 黄网站色欧美视频| 国产精品免费久久久| 欧美亚洲国产日本| 国产精品久久久久久久av大片| 亚洲国产高潮在线观看| 国产精品亚洲激情| 川上优av一区二区线观看| 国内精品在线一区| 亚洲精品视频在线观看视频| 九九热这里只有精品免费看| 国产亚洲成av人片在线观看桃| 欧美在线免费观看| 高清视频欧美一级| 91色精品视频在线| 欧美激情精品久久久久久久变态| 欧美性猛交xxxx富婆| 国产精品久久久久久久久久免费| 亚洲成色777777女色窝| 亚洲精品国产精品国产自| 成人性生交大片免费看视频直播| 国产精品99蜜臀久久不卡二区| 免费av在线一区| 久久亚洲国产精品| 精品国产户外野外| 亚洲美女免费精品视频在线观看| 中文字幕日韩精品在线| 精品在线小视频| 亚洲精品影视在线观看| 国产一区二区三区中文| 日韩欧美在线第一页| 丝袜一区二区三区| 欧美一区第一页| 欧美富婆性猛交| 91高清视频免费| 国产视频精品va久久久久久| 欧美精品精品精品精品免费| 午夜免费日韩视频| 欧美成人小视频| 亚洲第一在线视频| 亚洲欧美激情精品一区二区| 欧美人与物videos| 在线性视频日韩欧美| 91理论片午午论夜理片久久|