在學習上一個類TreeMap的時候,提到了這個類,這個類是jdk1.7新增的,裏面有不少實用的方法。就是一個工具類,熟悉之後,若是裏面有已經實現的方法,那麼就不要再去實現了,省時省力省測試。java
這是一個工具類,介紹相對會簡單些,基本都是方法的介紹。編程
Objects是一個主要針對對象的工具類,因此它的命名只是在後面加上一個s,就像Arrays是操做數組的工具類同樣。這就涉及到一種設計理念,那就是工具方法應該放在哪:數組
第一種不太建議,既然都是一個工具類,那麼就應該拿出來給別人用,或者本身之後用。less
第二種和第三種我比較不出來,我用的是第三種,創建一個大而全的Utils類,固然它們之間仍是能夠比較的:jvm
先看下面一部分代碼:ide
public final class Objects { private Objects() { throw new AssertionError("No java.util.Objects instances for you!"); } }
由於是工具類加上final不讓別人繼承,還有就是構造方法加上私有並拋異常。工具
說實話我以爲沒有必要這麼作,或許他們考慮的比較多,可是這麼作也沒什麼壞處,之後我寫工具類也這樣,顯得高大上一些。學習
Objects裏面提供兩種比較方式:測試
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); } public static boolean deepEquals(Object a, Object b) { if (a == b) return true; else if (a == null || b == null) return false; else return Arrays.deepEquals0(a, b); }
其中咱們廣泛使用的就是equals,而後我看deepEquals的源碼的時候,發現了一個以前忽略的事而後我作了測試:ui
int[] a1=new int[]{1}; int[] a2=new int[]{1}; System.out.println(a1.equals(a2));
結果是false,也就是說由於數組(以數字爲下標,不表明裏面的內容是數字)的equals都是false,除非它們是同一個對象。也就是說咱們之後若是要作兩個對象的相等判斷,若是比較的是兩個數組那麼記得使用Objects的deepEquals方法。
你們都知道在null值對象上調用任何方法都會報空指針異常,在一些須要判斷對象是不是null的狀況下,拋異常是有必要的,可是當能夠容許null值的時候,不少Object經常使用的方法就須要額外的處理,而Objects就幫咱們作了:
public static String toString(Object o) { return String.valueOf(o); } public static int hash(Object... values) { return Arrays.hashCode(values); } public static String toString(Object o, String nullDefault) { return (o != null) ? o.toString() : nullDefault; }
因此使用起來很方便,可是必定要注意使用場景,保證null值是有效值的時候使用。
不少時候,咱們須要保證入參的正確性,最簡單的就是傳入的不能是null,若是說上層須要將錯誤緣由展現給用戶看,咱們就須要主動檢查是否爲null,而且主動返回null值所表明的信息(好比說用戶名不能爲空啦,郵箱不能爲空啦),而不是拋異常。
可是當不須要給用戶看的時候,那就能夠直接將異常拋出來就能夠了,這就有兩種策略:
如今大多數人都不多主動去檢查null值,我以爲這是很差的編程習慣,我認爲應該在方法入口的時候主動檢查null值並拋異常,緣由以下:
因此建議之後寫代碼的時候多多主動檢查,使用
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; } public static <T> T requireNonNull(T obj, String message) { if (obj == null) throw new NullPointerException(message); return obj; }
上面說的好處你會慢慢體會到的。
如今先看下面兩個方法,一樣是判斷null值的:
public static <T> T requireNonNull(T obj, String message) { if (obj == null) throw new NullPointerException(message); return obj; } public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) { if (obj == null) throw new NullPointerException(messageSupplier.get()); return obj; }
傳入的message是在當參數是null值的時候傳入給異常使用的,可是爲何會有兩個方法呢,並且第二個看起來遠遠比第一個麻煩,咱們先看怎麼使用:
public void test1(String param){ Objects.requireNonNull(param, "param can not be null"); //do something about param }
上面是方法一調用的方式,很簡潔明瞭,下面是方法二的調用方式:
public void test2(String param){ Objects.requireNonNull(param, new Supplier<String>() { @Override public String get() { return "param can not be null"; } }); //do something about param } public void test3(String param){ Objects.requireNonNull(param, ()->"param can not be null"); //do something about param }
有兩種方式,第二種是jdk8的lambada表達式,若是不瞭解的話,但願抽時間學習一下仍是有必要的。
能夠看到延遲消息的調用要比普通消息複雜,可是爲何還要推薦這種方式呢?再舉一個例子,之前我看過以下記錄日誌的方式:
if(log.isDebugEnabled()){ log.debug("read file sucess, read info is :"+properties); }
當時我對這種代碼嗤之以鼻,我以爲明明log.debug在裏面已經判斷日誌級別了,爲何在外面還要在判斷一次,如今想一想當時仍是太年輕。若是你仔細想一想或許能發現,若是咱們提早判斷日誌級別那麼,當不符合日誌級別的時候咱們就不須要拼接字符串,而若是咱們直接用:
log.debug("read file sucess, read info is :"+properties);
用上面代碼的話,咱們就須要每次都要拼接完字符串後再進行判斷,而debug日誌基本上量都特別大,並且基本不會打印,這也就形成了字符串拼接過多,而字符串會放到常量池裏。
因此簡單總結下延遲消息的好處,那就是在須要的時候才進行拼接,這個在log4j2中已經提供相應的支持,可是這個好處到底有多少,我如今還不清楚。
這個類是一個工具類,不少方法用起來都很方便,若是用的好的話,應該能解決程序中出現的大部分NullPointerException,因此呢之後對經常使用的方法要作到封裝複用。
大體看來遺留一個未深刻研究的問題
上面1.6講解了延遲消息,我只知道延遲消息有好處,雖然jdk註釋中也明說了:
the costs of creating the message supplier are less than the cost of just creating the string message directly
建立延遲消息要比直接建立字符串高效,可是能高效多少呢,這是一個疑問,固然這也牽扯到jvm對字符串的處理。我會抽時間作以下深刻測試和研究:
固然研究和測試只是爲了驗證論點,這不能成爲不用這個技術的理由,之後的使用場景我仍是會使用延遲消息的。