摘要:模式下使用來設置各個參數,無法僅通過檢驗構造器參數的有效性來保證一致性,會試圖使用不一致狀態的對象。消除過期的對象引用緩存時優先使用,這些數據結構,及時清掉沒用的項。顯示取消監聽器和回調,或進行弱引用。
創建和銷毀對象
靜態工廠方法有名稱,能確切地描述正被返回的對象。
不必每次調用都創建一個新的對象。
可以返回原返回類型的任何子類對象。
創建參數化類型實例時更加簡潔,比如調用構造 HashMap 時,使用 Map
靜態工廠和構造器不能很好地擴展到大量的可選參數。
JavaBean 模式下使用 setter 來設置各個參數,無法僅通過檢驗構造器參數的有效性來保證一致性,會試圖使用不一致狀態的對象。
Builder 的建造者模式:使用必須的參數調用構造器,得到一個 Builder 對象,再在 builder 對象上調用類似 setter 的方法設置各個可選參數,最后調用無參的 build 方法生成不可變對象,new Instance.Builder(必須參數).setter(可選參數).build()。
Builder 模式讓類的創建和表示分離,使得相同的創建過程可以創建不同的表示。
對于 String 類型,String s = new String("") 每次執行時都會創建一個新的實例,而使用 String s = "" 則不會,因為對于虛擬機而言,包含相同的字符串字面常量會重用,而不是每次執行時都創建一個新的實例。
優先使用基本類型而不是裝箱的基本類型,避免無意識的自動裝箱。
緩存時優先使用 WeakHashMap,LinkedHashMap 這些數據結構,及時清掉沒用的項。
顯示取消監聽器和回調,或進行弱引用。
對于所有對象都通用的方法如果類具有自己特有的"邏輯相等",但超類還沒有覆蓋 equals 以實現期望的行為。
高質量equals的方法
使用 == 操作符檢查”參數是否為這個對象的引用“。
使用 instanceof 操作符檢查“參數是否為正確的類型”。
把參數轉換成正確的類型。
對于該類中的每個關鍵域,檢查參數中的域是否與該對象中對應的域相匹配。
不要將 equals 聲明的 object 對象替換為其他的類型,因為這樣是沒法覆蓋 Object.equals,只是提供了一個重載。
相等的對象必須具有相等的散列碼,如果沒有一起去覆蓋 hashcode,則會導致倆個相等的對象未必有相等的散列碼,造成該類無法結合所有基于散列的集合一起工作。
Object 提供的 toString,實現是類名+@+散列碼的無符號十六進制。
自己覆蓋的 toString,返回對象中包含的所有值得關注的信息。
不足:當類被廣泛使用,一旦指定格式,那就會編寫出相應的代碼來解析這種字符串表示法,以及把字符串表示法嵌入持久化數據中,之后若改變這種表示法,則會遭到破壞。
如果類實現了comparable 接口,便可以跟許多泛型算法以及依賴該接口的集合實現協作,比如可以使用 Array.sort 等集合的排序。
類和接口隱藏內部實現細節,有效解耦各模塊的耦合關系
訪問級別
private:類內部才可訪問
package-private(缺省的):包內部的任何類可訪問
protected:聲明該成員的類的子類以及包內部的類可訪問
public:任何地方均可訪問
繼承打破了封裝性,除非超類是專門為了擴展而設計的。超類若在后續的發行版本中獲得新的方法,并且其子類覆蓋超類中與新方法有關的方法,則可能會發生錯誤。
復合:在新的類中增加一個私有域,引用現有類。它不依賴現有類的實現細節,對現有類進行轉發。
抽象類允許包含某些方法的實現,但為了實現由抽象類定義的類型,類必須成為抽象類的一個子類,且是單繼承。
接口允許我們構造非層次結構的類型框架,安全地增強類的功能。
對每個重要的接口都提供一個抽象的骨架實現類,把接口和抽象類的優點結合(接口不能包含具體的方法,抽象類使用繼承來增加功能)。它們為抽象類提供了實現上的幫助,但又不強加抽象類被用作類型定義時所特有的嚴格限制。
抽象類的演變比接口的演變要容易得多,在后續版本中在抽象類中始終可以增加新的具體方法,其抽象類的所有子類都將提供這個新的方法,而接口不行。
當類實現接口時,接口充當可以引用這個類的實例的類型,為了任何其他目的而定義接口時不恰當的。
常量接口時對接口的不良使用。實現常量接口,會導致把這樣的實現細節泄漏給該類的導出 API 中,當類不再需要這些常量時,還必須實現這個接口以確保兼容性。如果非final類實現了該常量接口,它的所有子類的命名空間都將被接口中的常量污染。
靜態成員類是最簡單的嵌套類,可以當做普通的類,只是被聲明在另一個類的內部。
非靜態成員類的每個實例都隱含著與外部類的一個外部實例相關聯。沒有外部實例的情況下,是無法創建非靜態成員類的實例。每個非靜態成員類的實例都包含一個額外的指向外部對象的引用,會導致外部實例在垃圾回收時仍然保留。
匿名類沒有名字,在使用的同時被聲明和實例化。當匿名類出現在非靜態環境中時有外部實例,在靜態環境中也不能擁有任何靜態成員。匿名類必須保持簡短,保持可讀性。
局部類,在任何可以聲明局部變量的地方聲明局部類,有名字,在非非靜態環境中定義才有外部實例,不能包含靜態成員,同時必須保持簡短。
枚舉和注解枚舉類型是指由一組固定的常量組成合法值的類型,通過公有的靜態 final 域為每個枚舉常量導出實例的類,沒有構造器,是單例的泛型化。
int 枚舉模式在類型安全性和使用方便性沒有任何幫助,打印的 int 枚舉變量只是一個數字。
String 枚舉模式雖然提供了可打印的字符串,但會導致性能問題,還依賴于字符串的比較操作。
枚舉類型可以通過 toString 將枚舉轉換成可打印的字符串,還允許添加任意的方法和域,并實現任意的接口。
性能缺點:裝載和初始化枚舉時會有空間和時間的成本。
方法對于公有方法,用 Javadoc 的 @throw 標簽在文檔中說明違反參數限制時會拋出的異常。
對于未被導出的方法(私有的),可以使用斷言來檢查參數。斷言如果失敗會拋出 AssertionException,如果沒起到作用也不會有成本開銷。
每當編寫方法或構造器時,要考慮它的參數有哪些限制,應該把這些限制寫到文檔中,并且在方法體的開頭處進行顯示的檢查。
對方法的每個可變參數,或返回一個指向內部可變組件的引用時,需要進行保護性拷貝,避免在使用過程中可變對象進行了修改。
保護性拷貝是在檢查參數的有效性之前進行的,并且有效性檢查是針對拷貝之后的對象。
重載方法的選擇是靜態的,選擇工作時在編譯時進行,完全基于參數的編譯時類型。
覆蓋方法的選擇是動態的,選擇的依據是被調用方法所在對象的運行時類型。
不要導出倆個具有相同參數數目的重載方法,如果參數數目相同,則至少有一個對應的參數在倆個重載方法中具有根本不同的類型,否則就應該保證,當傳遞同樣的參數時,所有的重載方法的行為必須一致。
對于返回 null 而不是零長度數組或集合的方法,幾乎每次用到該方法時都需要進行 null 值的判斷,這樣很曲折同時很容易出錯。
通用程序設計基本類型只有值,而裝箱基本類型可以具有相同的值和不同的同一性。對裝箱基本類型運用 == 操作符幾乎總是錯誤的。
基本類型只有功能完備的值,而每個裝箱基本類型除了它對應的基本類型的所有功能值外,還有個非功能值:null。當在一項操作中混合使用基本類型和裝箱基本類型時,裝箱基本類型會自動拆箱,如果 null 對象引用被自動拆箱,會得到空指針異常。
基本類型通常比裝箱基本類型更節省時間和空間,裝箱基本類型會導致高開銷和不必要的對象創建。
字符串是不可變的,當倆個字符串連接時需要對其內容進行拷貝,連接 n 個字符串需要 n 的平方級時間。因為第 n 次拼接的字符串,需要 n-1 次的字符串和第 n 次的字符串拷貝,和他們拼接后的拷貝,這樣 an - an-1 = n-1+1+n = 2n;這樣可以得到 an = n*(n-1),及 O(N^2) 的拼接時間。
如果有合適的接口類型存在,那么對于參數、返回值、變量和域來說,就都應該使用接口類型進行聲明。如,List<>vector = new Vector<>();List list = new ArrayList<>(); ,這樣程序會更加靈活,當更換實現時,所要做的只是改變構造器中的類。
如果沒有合適的接口存在,完全可以用類而不是類接口來引用對象。如果含有基類,則優先使用基類來引用這個對象而不是它的實現類。
異常異常是為了在異常情況下使用而設計的,不要將他們用于普通的控制流,而不要編寫破事他們這么做的 API。
基于異常的循環模式不僅模糊了代碼的意圖,降低了性能( JVM 不會對異常的代碼塊進行優化),而且它還不能保證正常工作。
受檢異常:如果期望調用者能適當地恢復,這時應該使用受檢的異常。通過拋出受檢的異常,強迫調用者在一個 catch 中處理該異常或傳播出去。
未受檢異常:不需要也不應該被捕獲的可拋出結構。
運行時異常:表明編程錯誤,是 RuntimeException 的子類,運行時檢查。
錯誤:表示資源不足,約束失敗,或其他使程序無法繼續執行的條件。
設計受檢異常拋出 API 的條件:正確地使用 API 不能阻止這種異常條件的產生 & 產生異常后可以立即采取有用的動作。
當方法傳遞由低層抽象拋出的異常與所執行的任務沒有明顯聯系時,會導致困擾且讓實現細節污染了更高層 API。
更高層的實現應該捕獲低層的異常,同時拋出可以按照高層抽象進行解釋的異常(異常轉譯)。
失敗原子性:失敗的方法調用應該使對象保持在被調用之前的狀態。
設計不可變對象,永遠不會使已有的對象保持在不一致的狀態中。
對于可變對象:
執行操作之前檢查參數的有效性。
調整計算處理過程的順序,使得任何可能失敗的計算部分都在對象狀態被修改之前發生。
編寫一段恢復代碼,由它來攔截操作過程中發生的失敗,以及對象回滾到操作開始之前的狀態上,主要用于永久性的數據結構。
在對象的一份臨時拷貝上執行操作,不破壞傳入對象的狀態。
并發同步可以阻止一個線程看到對象處于不一致的狀態之中,還能保證進入同步方法或者同步代碼塊的每個線程,都看到由同一個鎖保護的之前所有的修改效果。
多個線程共享可變數據時,每個讀或者寫數據的線程都必須執行同步,否則可能導致活性失敗和安全性失敗。
活性失敗:線程A對某變量值的修改,可能沒有立即在線程B體現出來。
安全性失敗:并發訪問共享資源導致狀態不一致造成的安全問題。
過度同步可能會導致性能降低、死鎖,甚至不確定的行為。
在同步區域內做盡可能少的工作,過度的同步會丟失并行的機會,限制 VM 優化代碼執行的能力
不要從同步區域內部調用外來方法,避免死鎖和數據破壞。
CopyOnWriteArrayList 通過重新拷貝整個底層數組實現所有的寫操作,適用于讀操作遠大于寫操作的場景,當寫操作頻繁時性能損耗很大。
序列化一旦一個類被發布,就大大降低了“改變這個類的實現” 的靈活性。若接受了默認的序列化形式,并且以后要改變類的內部結構,會導致序列化形式的不兼容。其次序列化對應流的唯一標識符 UID,在沒有顯示聲明序列版本 UID,那么改變類的信息,將產生新的序列版本 UID,破壞它的兼容性。
增加了出現 bug 和安全漏洞的可能性。反序列化機制中沒有顯示的構造器,很容易忘記要確保:反序列化過程必須要保證所有“由真正的構造器建立起來的約束關系”,并且不允許攻擊者訪問正在構造過程中的對象的內部信息。
測試負擔增加。當一個可序列號的類被修訂時,需要檢查“在新版本中序列化一個實例,然后再舊版本中反序列號”,反之亦然,這種測試不可自動構建,測試工作量與“可序列化的類的數量和發行版本號”的乘積成正比。
本文發表于個人博客:http://lavnfan.github.io/,歡迎指教。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70342.html
摘要:這本書是我第一次買的,從買來至今整本書還沒有看完,只看了一半,原因是個人比較懶,而且玩的心比較大,經過這么多年的沉淀,終于可以偷點時間寫下對于這本書的觀后感了整本書給我的感覺不像是一個技術書,更多的是講解一些實用技巧,而對于我這個職場菜鳥來 effective Java 這本書是我第一次買的, 從買來至今整本書還沒有看完, 只看了一半, 原因是個人比較懶,而且玩的心比較大,經過這么多年...
摘要:三進階階段這個階段主要是靠我們自己學習總結,可以通過前輩們的博客或者自己研究源碼,這些非常有利于我們快速的成長。讓自己保持永遠學習的精神。五零基礎學習資料最后給大家準備了一份不錯的學習資源,里面有很多學習視頻和資料,后臺回復資源,即可獲取。 showImg(https://segmentfault.com/img/bVbauV8?w=1212&h=816); 前兩次給大家分享了關于 j...
摘要:實戰高并發程序設計這本書是目前點評推薦比較多的書,其特色是案例小,好實踐代碼有場景,實用。想要學習多線程的朋友,這本書是我大力推薦的,我的個人博客里面二十多篇的多線程博文都是基于此書,并且在這本書的基礎上進行提煉和總結而寫出來的。 學習的最好途徑就是看書,這是我自己學習并且小有了一定的積累之后的第一體會。個人認為看書有兩點好處:showImg(/img/bVr5S5); 1.能出版出...
摘要:你應當了解的位牛人編譯李雋龍。擁有四項發明專利,據稱他的凈資產值高達億美元。年月日,在其博客上宣布調離安卓部門并將承擔谷歌公司新的項目。年月日,通知執行委員會他將不再參選。后來,又重新當選了理事會的全權代表。 Java領域有很多著名的人物,他們為Java社區編寫框架、產品、工具或撰寫書籍改變了Java編程的方式。本文是《最受歡迎的8位Java牛人》的2.0版本。 ChangLo...
閱讀 4028·2021-11-22 13:53
閱讀 1729·2021-09-23 11:52
閱讀 2445·2021-09-06 15:02
閱讀 955·2019-08-30 15:54
閱讀 911·2019-08-30 14:15
閱讀 2392·2019-08-29 18:39
閱讀 663·2019-08-29 16:07
閱讀 427·2019-08-29 13:13