李敏 |
|
|||
日歷
統計
導航常用鏈接留言簿(1)文章分類
文章檔案
相冊收藏夾它山之石聚賢莊搜索最新評論
|
在Java編碼中,我們容易犯一些錯誤,也容易疏忽一些問題,因此筆者對日常編碼中曾遇到的一些經典情形歸納整理成文,以共同探討。
1. 糾結的同名現象 很多類的命名相同(例如:常見于異常、常量、日志等類),導致在import時,有時候張冠李戴,這種錯誤有時候很隱蔽。因為往往同名的類功能也類似,所以IDE不會提示warn。 解決 寫完代碼時,掃視下import部分,看看有沒有不熟悉的。替換成正確導入后,要注意下注釋是否也作相應修改。 啟示 命名盡量避開重復名,特別要避開與JDK中的類重名,否則容易導入錯,同時存在大量重名類,在查找時,也需要更多的辨別時間。 2. 想當然的API現象 有時候調用API時,會想當然的通過名字直接自信滿滿地調用,導致很驚訝的一些錯誤: 示例一:flag是true? boolean flag = Boolean.getBoolean("true"); 可能老是false。 示例二:這是去年的今天嗎(今年是2012年,不考慮閏年)?結果還是2012年: Calendar calendar = GregorianCalendar.getInstance(); calendar.roll(Calendar.DAY_OF_YEAR, -365); 下面的才是去年: calendar.add(Calendar.DAY_OF_YEAR, -365); 解決辦法 問自己幾個問題,這個方法我很熟悉嗎?有沒有類似的API? 區別是什么?就示例一而言,需要區別的如下: Boolean.valueOf(b) VS Boolean.parseBoolean(b) VS Boolean.getBoolean(b); 啟示 名字起的更詳細點,注釋更清楚點,不要不經了解、測試就想當然的用一些API,如果時間有限,用自己最為熟悉的API。 3. 有時候溢出并不難現象 有時候溢出并不難,雖然不常復現: 示例一: long x=Integer.MAX_VALUE+1; System.out.println(x); x是多少?竟然是-2147483648,明明加上1之后還是long的范圍。類似的經常出現在時間計算:
示例二: 在檢查是否為正數的參數校驗中,為了避免重載,選用參數number, 于是下面代碼結果小于0,也是因為溢出導致: Number i=Long.MAX_VALUE; System.out.println(i.intValue()>0); 解決
啟示 對數字運用要保持敏感:涉及數字計算就要考慮溢出;涉及除法就要考慮被除數是0;實在容納不下了可以考慮BigDecimal之類。 4. 日志跑哪了?現象 有時候覺得log都打了,怎么找不到? 示例一:沒有stack trace! } catch (Exception ex) { log.error(ex); } 示例二:找不到log! } catch (ConfigurationException e) { e.printStackTrace(); } 解決
啟示
5. 遺忘的volatile現象 在DCL模式中,總是忘記加一個Volatile。 private static CacheImpl instance; //lose volatile public static CacheImpl getInstance() { if (instance == null) { synchronized (CacheImpl.class) { if (instance == null) { instance = new CacheImpl (); } } } return instance; } 解決 毋庸置疑,加上一個吧,synchronized 鎖的是一塊代碼(整個方法或某個代碼塊),保證的是這”塊“代碼的可見性及原子性,但是instance == null第一次判斷時不再范圍內的。所以可能讀出的是過期的null。 啟示 我們總是覺得某些低概率的事件很難發生,例如某個時間并發的可能性、某個異常拋出的可能性,所以不加控制,但是如果可以,還是按照前人的“最佳實踐”來寫代碼吧。至少不用過多解釋為啥另辟蹊徑。 6. 不要影響彼此現象 在釋放多個IO資源時,都會拋出IOException ,于是可能為了省事如此寫: public static void inputToOutput(InputStream is, OutputStream os, boolean isClose) throws IOException { BufferedInputStream bis = new BufferedInputStream(is, 1024); BufferedOutputStream bos = new BufferedOutputStream(os, 1024); …. if (isClose) { bos.close(); bis.close(); } } 假設bos關閉失敗,bis還能關閉嗎?當然不能! 解決辦法 雖然拋出的是同一個異常,但是還是各自捕獲各的為好。否則第一個失敗,后一個面就沒有機會去釋放資源了。 啟示 代碼/模塊之間可能存在依賴,要充分識別對相互的依賴。 7. 用斷言取代參數校驗現象 如題所提,作為防御式編程常用的方式:斷言,寫在產品代碼中做參數校驗等。例如: private void send(List< Event> eventList) { assert eventList != null; } 解決 換成正常的統一的參數校驗方法。因為斷言默認是關閉的,所以起不起作用完全在于配置,如果采用默認配置,經歷了eventList != null結果還沒有起到作用,徒勞無功。 啟示 有的時候,代碼起不起作用,不僅在于用例,還在于配置,例如斷言是否啟用、log級別等,要結合真實環境做有用編碼。 8. 用戶認知負擔有時候很重現象 先來比較三組例子,看看那些看著更順暢? 示例一: public void caller(int a, String b, float c, String d) { methodOne(d, z, b); methodTwo(b, c, d); } public void methodOne(String d, float z, String b) public void methodTwo(String b, float c, String d) 示例二: public boolean remove(String key, long timeout) { Future< Boolean> future = memcachedClient.delete(key); public boolean delete(String key, long timeout) { Future< Boolean> future = memcachedClient.delete(key); 示例三: public static String getDigest(String filePath, DigestAlgorithm algorithm) public static String getDigest(String filePath, DigestAlgorithm digestAlgorithm) 解決
啟示 在編碼過程中,不管是參數的順序還是命名都盡量統一,這樣用戶的認知負擔會很少,不要要用戶容易犯錯或迷惑。例如用枚舉代替string從而不讓用戶迷惑到底傳什么string, 諸如此類。 9. 忽視日志記錄時機、級別現象 存在下面兩則示例: 示例一:該不該記錄日志? catch (SocketException e) { LOG.error("server error", e); throw new ConnectionException(e.getMessage(), e); } 示例二:記什么級別日志? 在用戶登錄系統中,每次失敗登錄: LOG.warn("Failed to login by "+username+"); 解決
啟示 日志改不改記?記成什么級別?如何記?這些都是問題,一定要根據具體情況,需要考慮:
10. 忘設初始容量現象 在JAVA中,我們常用Collection中的Map做Cache,但是我們經常會遺忘設置初始容量。 cache = new LRULinkedHashMap< K, V>(maxCapacity); 解決 初始容量的影響有多大?拿LinkedHashMap來說,初始容量如果不設置默認是16,超過16×LOAD_FACTOR,會resize(2 * table.length),擴大2倍:采用 Entry[] newTable = new Entry[newCapacity]; transfer(newTable),即整個數組Copy, 那么對于一個需要做大容量CACHE來說,從16變成一個很大的數量,需要做多少次數組復制可想而知。如果初始容量就設置很大,自然會減少resize, 不過可能會擔心,初始容量設置很大時,沒有Cache內容仍然會占用過大體積。其實可以參考以下表格簡單計算下, 初始時還沒有cache內容, 每個對象僅僅是4字節引用而已。
啟示 不僅是map, 還有stringBuffer等,都有容量resize的過程,如果數據量很大,就不能忽視初始容量可以考慮設置下,否則不僅有頻繁的 resize還容易浪費容量。 在Java編程中,除了上面枚舉的一些容易忽視的問題,日常實踐中還存在很多。相信通過不斷的總結和努力,可以將我們的程序完美呈現給讀者。
|
![]() |
|
Copyright © 李敏 | Powered by: 博客園 模板提供:滬江博客 |