為什么說一個好的員工能頂 100 個普通員工
我們的做法是,要用最好的人。我一直都認為研發本身是很有創造性的,如果人不放松,或不夠聰明,都很難做得好。你要找到最好的人,一個好的工程師不是頂10個,是頂100個。所以,在核心工程師上面,大家一定要不惜血本去找,千萬不要想偷懶只用培養大學生的方法去做。最好的人本身有很強的驅動力,你只要把他放到他喜歡的事情上,讓他自己有玩的心態,他才能真正做出一些事情,打動他自己,才能打動別人。所以你今天看到我們很多的工程師,他自己在邊玩邊創新。所以,找最好的人,要給他做他喜歡和擅長的事情。研發人員千萬不要去管太嚴,一管就“死”了。工程師很討厭跟規章制度打交道,作匯報他都很煩,大家不要管他,讓用戶去管他。他做好了一個產品,用戶表揚他,這個大神多牛逼。他做不好了,用戶罵他,他自己趕緊去改。
再談阿里巴巴 Java 開發手冊
之前在這個手冊剛發布的時候看過一遍,當時感覺真是每個開發者都應該必讀的一本手冊,期間還寫過一篇關於日志規約的文章:《下一個項目為什么要用 SLF4J》,最近由於在總結一些我們日常開發中容易忽略的問題,可能是最低級的編碼常見問題,往往這也是最最容易忽略的,所以,又重新看了一遍這個手冊,好像最近它也更新到了 1.2 版本。
這個手冊目的就是讓我們盡可能少踩坑,杜絕踩重復的坑。我接下來就打算試着寫一些“坑”出來,來看看我們如何一不留神踩坑的,以及如何用正確的姿勢跳出坑。
隨隨便便寫出 NPE
首先聲明一個 User
對象,接下來所有代碼可能都會用到這個對象做演示,在下面將不在贅述。很簡單,不上代碼,上圖片:
1.自動解箱拋 NPE
代碼只有一行,再簡單不過了:int method() { return new User().getId(); }
踩坑姿勢:包裝類型為 null 時,進行自動轉換為基本數據類型報錯。
解決方案:返回之前進行判斷與處理或者改為相同類型。
2.級聯調用易產生 NPE
這段代碼有點容易迷惑人,因為它進行了集合元素的 isEmpty
判斷,按說不會出問題了吧。看代碼:
static void method1() {
List<User> list = new ArrayList<User>();
list.add(new User());
if (!CollectionUtils.isEmpty(list)) {
for (User user : list) {
System.out.println("userid:" + user.getId().toString());
}
}
}
不廢話,看運行結果:
沒錯,還是報錯了。
踩坑姿勢:其實就是盡管你在之前做了對象不為空的判斷,但你並不能保證對象中的值不為空,而且這時候去級聯調用就會拋 NPE 。
手冊中關於 NPE 的描述:
防止 NPE 是調用者的責任。即使被調用方法返回空集合或者空對象,對調用者來說,也並非高枕無憂,必須考慮到遠程調用失敗、序列化失敗、運行時異常等場景返回 null 的情況。
集合里的元素即使 isEmpty,取出的數據元素也可能為 null。
級聯調用 obj.getA().getB().getC();一連串調用,易產生 NPE
3.關於 Equals
這是日常開發中用於相等比較使用最多的方法了吧,因為當年誰沒被 ==
坑過阿。現在一般我們都會這么寫:user.getName().equals("mafly");
踩坑姿勢: 一不小心使用了 null 值調用了 Equals
方法。
解決方案: 很簡單咯,這么寫:"mafly".equals(user.getName());
equals 方法容易拋空指針異常,應使用常量或確定有值的對象來調用 equals。
4.Map 下的 NPE
Map
應該是我們開發中使用最頻繁的了,最常用的可能有 HashMap
、ConcurrentHashMap
這倆了,可能會一不留神寫出這樣的代碼:
踩坑姿勢: 可能我們知道 ConcurrentHashMap
的 K/V 都不能為空,但我們有時候並不知道傳進來的值是否為空。
解決方案: 設置時做下檢驗,對它的特性正確理解及使用。
由於 HashMap 的干擾,很多人認為 ConcurrentHashMap 是可以置入 null 值,而事實上,
存儲 null 值時會拋出 NPE 異常
Map 類集合 K/V 能不能存儲 null 值的情況,如下表格:
集合類 | Key | Value | Super | 說明 |
---|---|---|---|---|
Hashtable | 不允許為 null | 不允許為 null | Dictionary | 線程安全 |
ConcurrentHashMap | 不允許為 null | 不允許為 null | AbstractMap | 分段鎖技術 |
TreeMap | 不允許為 null | 允許為 null | AbstractMap | 線程不安全 |
HashMap | 允許為 null | 允許為 null | AbstractMap | 線程不安全 |
簡單聊聊常用的集合
5.foreach 遍歷集合刪除元素
大家應該都知道,在遍歷集合時對元素進行 add/remove 操作要使用 Iterator,使用 for 循環時會報錯,一定會報錯嗎?看代碼:
public static void main(String[] args) {
List<String> a = new ArrayList<>();
a.add("1");
a.add("2");
a.add("3");
for (String temp : a) {
if ("2".equals(temp)) {
a.remove(temp);
}
}
Iterator<String> it = a.iterator();
while (it.hasNext()) {
String temp = it.next();
if ("2".equals(temp)) {
it.remove();
}
}
}
應該會報錯的吧?因為在 for 循環中移出了元素,如果你運行了就會驚訝的,輸出如下:
不解釋其中原因了,感興趣的可以看這篇文章:《foreach遍歷list刪除元素一定會報錯?》。不管是不是倒數第二個元素才沒問題,我們依然要注意不要在 foreach 循環里進行元素的 remove/add 操作。remove 元素請使用 Iterator 方式(代碼第二種),如果並發操作,需要對 Iterator 對象加鎖。
6.Arrays.asList() 數組轉換集合
這個工具類應該都用過,可以很方便的把數組轉換為集合,直接看結果吧:
踩坑姿勢: Arrays.asList()
把數組轉換成集合時,不能使用其修改集合相關的方法,它的 add/remove/clear 方法會拋出 UnsupportedOperationException
異常。 asList()
的返回對象是一個 Arrays 內部類,並沒有實現集合的修改方法。
解決方案: 在轉換之前操作咯。還需要注意一點,在你轉換后,再對數組的值進行修改時,集合也會跟着變哦(注釋掉的代碼)。
7. toArray() 集合轉換數組
當我們需要把一個集合轉換為數組時,往往會調用 toArray()
方法,如果你用的是無參的這個可以嗎?
當然不可以啦!會報 ClassCastException
異常。
踩坑姿勢: 直接使用 toArray()
無參方法返回值只能是 Object[]類,若強轉其它類型數組將會拋異常。
解決方案: 使用 <T> T[] toArray(T[] a);
有參數這個方法,代碼如下:
String[] array = new String[list.size()];
array = list.toArray(array);
8. subList 的使用
集合中的 subList
是用於來返回某一部分的視圖內容的,可能我們不是很常用,但是其中有好多坑的,直接看代碼:
這次我們從輸出來看上面的所有關於 subList
的代碼。
- 18行: 當你原始集合大小沒有那么大時,毫無疑問拋異常。
- 20-21行:得到一個新的集合,我們往新集合中增加一條數據。
- 23-26行:遍歷原始集合,竟然
size=2
了,而且往新集合中增加的數據存在與原始集合。 - 28-31行:移除新集合中一條數據,遍歷新集合。
- 33-37行:原始集合增加一條數據並遍歷。
- 40-42行:遍歷新集合,拋出
ConcurrentModificationException
異常。
從上述代碼中,我們應該可以得出如下結論:返回的新集合是靠原來的集合支持的,修改都會影響到彼此對方。在 subList 場景中,高度注意對原集合元素個數的修改,會導致子列表的遍歷、增加、刪除均產生異常。
先總結一下
寫到這只是其中關於異常部分的一些坑吧,還有另外一些令人異常驚訝的“我的天吶”的問題,由於篇幅太長了點,感覺不能再寫下去了,過兩天再接着寫吧。
異常真的是一個有意思的問題。