寫Java代碼有三年多了,遇到過很多坑,也有一些小小的心得。特地分享出來供各位學習交流。這些技巧主要涉及谷歌Guava工具類的使用、Java 8新特性的使用、DSL風格開發、代碼封裝等技巧。
一、null的判斷
對於Java Developer來說空指針異常讓我們深惡痛絕。我們進行單元測試的時候很多的時間就是在消除空指針異常,一個容易報空指針的代碼肯定稱不上是合格的代碼。當然,完全消除空指針絕非易事,但我們可以最大限度地讓空指針異常變得可控。只要我們知道代碼何時會出現null,那么我們就成功一大半了。
1. String中的空指針異常
String是使用很多的類型,但是使用頻繁的往往也更容易出現null的問題。
public String test(String id){
String result=userService.findNameById(id);
return result.toUpperCase();
}
上面這段代碼有幾個不確定的地方,因為id可能為null
,與此同時dinNameById的方法也可能返回null,那么最后result.toUpperCase()這個方法就有可能出現空指針異常。這個問題的解決方法很簡單,我稍微改進了一點代碼
public String test( String id ){
if ( id != null && !"".equals(id)){
String result = userService.findNameById( id );
if ( result != null ){
return result.toUpperCase();
}
return null;
}
return null;
}
我們對Id進行了null判斷和empty判斷,確保id是合法的。對查找的結果進行null判斷,確保我們不會對null對象進行任何操作,這是因為.
操作是空指針的重災區。那么這個方法返回null這個操作是安全的嗎?
答案是安全的。顯示地返回null,可以讓其他人使用這個方法的時候有意地進行null判斷,從而避免出現空指針的問題。還是那句話避免空指針異常並不是不使用null,而是讓null變得可控。
2.使用Guava工具包的Strings類
上面的代碼里我們有一行代碼是這樣寫的
if ( id != null && !"".equals(id))
如果大量這么寫的話我們會覺得十分繁瑣,這種重復的工作是可以簡化的。
比如我們可以定義一個StringUtil類,里面寫一個公共方法來判斷null或empty。Guava工具包的Strings類已經為我們寫好了,我十分建議使用穩定可靠的Guava工具類來代替自己手寫的工具類。使用Guava我們只需要這樣寫:
if(!Strings.isNullOrEmpty(id))
比之前簡單了很多吧。
二、如何判斷相等
對於基本類型我們用==
判斷就可以,如果是String類型我們使用equals
,這個是很基礎的知識了。那么我們怎么判斷兩個對象是否相等呢?
對於集合類的對象,我們可以遍歷對象中的每個數據,逐一判斷是否相等,這是簡單粗暴的方式。那么如果我們判斷兩個class是否相等該怎么做呢?答案是用hashcode。
if(obj1.toString().hashCode()==obj2.toString().hashCode())
這里的重點是你比較的對象必須先轉成String串,然后比較String串的hashcode。因為直接比較對象的hashcode那是肯定不一樣的。
三、靈活使用Stream
Stream是JDK8的新特性,任何標注了@FunctionalInterface
的接口都能使用Stream流來處理數據。
很多同學知道函數式編程很強大,也知道怎么用,但是實際應用起來總會摸不着頭腦。
1.遍歷
只要了解了Stream的使用基本都會用它來遍歷。但是什么時候用map,什么時候用forEach,這是個問題。
其實實際項目中我們遍歷一個集合類,無非是兩類操作。第一種是對集合中元素的本身進行操作(如字段值的修改),第二種是消費集合中的元素,比如打印每個元素的某個值,或者讓另一個方法使用元素。當然,還有第三種,就是過濾、聚合、排序,這些都是比較簡單的,我們這里不談。
針對第一種我們應該把這種使用成為Function,這是一種給定T對象,返回R的函數式接口。詳情請看我之前的文章Function接口的使用。第二種其實是Consumer,也就是消費者。這是給定T,但不返回值的函數式接口。
其實我們只看map里面的參數也知道,第一類的操作用map就行了。
第二種顯然直接forEach就可以了。
這個技巧是通用的,如果你想實現什么操作,但不知道該用什么接口的時候,直接看文檔就知道該用哪個接口了。
四、鍵值對的key和value互換一下?
List<String> DBTypes = Arrays.asList("Oracle", "SYBASE","MYSQl子對象");
List<String> JMXTypes = Arrays.asList( "消息中間件", "JBoss子對象", "Tomcat子對象", "Apache子對象");
List<String> HOSTTypes = Arrays.asList("HOST", "主機子對象");
List<String> WMITypes = Arrays.asList("Windows單進程", "Windows");
像上面這樣的場景你肯定遇到過。幾個子類型,對應一個父類型,這可能有多個集合。那么如果我需要根據一個子類型,找到它對應的父類型,我就要遍歷每個集合的子類型。而且由於HashMap的鍵不能重復,因此這種數據沒有辦法轉成Map來操作。
這時候我們可以調換下思路,HashMap中的鍵不能重復,但是值是可以重復的。我們完全可以把上面所有的子類型當做key,其父類型當做value,存儲到一個Map中去。下面我使用stream來把List轉為Map:
Map<String, String> map = Maps.newHashMap();
map.putAll(DBTypes.stream().collect(Collectors.toMap(Function.identity(), i -> "JDBC")));
map.putAll(JMXTypes.stream().collect(Collectors.toMap(Function.identity(), i -> "JMX")));
map.putAll(HOSTTypes.stream().collect(Collectors.toMap(Function.identity(), i -> "HOST")));
map.putAll(WMITypes.stream().collect(Collectors.toMap(Function.identity(), i -> "WMI")));
於是所有的子類型和父類型的對應關系就都存到一個Map中去了,我們找起來就太方便了,一行代碼搞定!
String parentType=map.get(name);