Java程式設計中最容易忽略的10個問題
在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的範圍。類似的經常出現在時間計算:
數字1×數字2×數字3…
示例二:
在檢查是否為正數的引數校驗中,為了避免過載,選用引數number, 於是下面程式碼結果小於0,也是因為溢位導致:
Number i=Long.MAX_VALUE; System.out.println(i.intValue()>0);
解決
- 讓第一個運算元是long型,例如加上L或者l(不建議小寫字母l,因為和數字1太相似了);
- 不確定時,還是使用過載吧,即使用doubleValue(),當引數是BigDecimal引數時,也不能解決問題。
啟示
對數字運用要保持敏感:涉及數字計算就要考慮溢位;涉及除法就要考慮被除數是0;實在容納不下了可以考慮BigDecimal之類。
4. 日誌跑哪了?
現象
有時候覺得log都打了,怎麼找不到?
示例一:沒有stack trace!
} catch (Exception ex) { log.error(ex); }
示例二:找不到log!
} catch (ConfigurationException e) { e.printStackTrace(); }
解決
- 替換成log.error(ex.getMessage(),ex);
- 換成普通的log4j吧,而不是System.out。
啟示
- API定義應該避免讓人犯錯,如果多加個過載的log.error(Exception)自然沒有錯誤發生
- 在產品程式碼中,使用的一些方法要考慮是否有效,使用e.printStackTrace()要想下終端(Console)在哪。
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)
解決
- 保持引數傳遞順序;
- remove變成了delete,顯得突兀了點, 統一表達更好;
- 保持表達,少縮寫也會看起來流暢點。
啟示
在編碼過程中,不管是引數的順序還是命名都儘量統一,這樣使用者的認知負擔會很少,不要要使用者容易犯錯或迷惑。例如用列舉代替string從而不讓使用者迷惑到底傳什麼string, 諸如此類。
9. 忽視日誌記錄時機、級別
現象
存在下面兩則示例:
示例一:該不該記錄日誌?
catch (SocketException e) { LOG.error("server error", e); throw new ConnectionException(e.getMessage(), e); }
示例二:記什麼級別日誌?
在使用者登入系統中,每次失敗登入:
LOG.warn("Failed to login by "+username+");
解決
- 移除日誌記錄:在遇到需要re-throw的異常時,如果每個人都按照先記錄後throw的方式去處理,那麼對一個錯誤會記錄太多的日誌,所以不推薦如此做;但是如果re-throw出去的exception沒有帶完整的trace( 即cause),那麼最好還是記錄下。
- 如果惡意登入,那系統內部會出現太多WARN,從而讓管理員誤以為是程式碼錯誤。可以反饋使用者以錯誤,但是不要記錄使用者錯誤的行為,除非想達到控制的目的。
啟示
日誌改不改記?記成什麼級別?如何記?這些都是問題,一定要根據具體情況,需要考慮:
- 是使用者行為錯誤還是程式碼錯誤?
- 記錄下來的日誌,能否能給別人在不造成過多的干擾前提下提供有用的資訊以快速定位問題。
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位元組引用而已。
- memory for reference fields (4 bytes each);
- memory for primitive fields
Java type | Bytes required |
boolean | 1 |
byte | |
char | 2 |
short | |
int | 4 |
float | |
long | 8 |
double |
啟示
不僅是map, 還有stringBuffer等,都有容量resize的過程,如果資料量很大,就不能忽視初始容量可以考慮設定下,否則不僅有頻繁的 resize還容易浪費容量。
在Java程式設計中,除了上面列舉的一些容易忽視的問題,日常實踐中還存在很多。相信通過不斷的總結和努力,可以將我們的程式完美呈現給讀者。
相關文章
- Java程式設計中最容易踩雷的地方!Java程式設計
- 解讀C#程式設計中最容易忽略7種編寫習慣!C#程式設計
- Oracle中最容易被忽略的那些實用特性Oracle
- 遊戲設計師在開發中最容易犯下的錯誤/最容易忽略的地方是什麼?遊戲設計師
- 有哪些錯是Java程式設計師在面試中最容易犯的呢?Java程式設計師面試
- JS:關於JS字面量及其容易忽略的12個小問題JS
- 分享一些Java開發人員在程式設計中最容易踩雷的地方!Java程式設計
- 好程式設計師Java教程分享Java中String型別的10個問題程式設計師Java型別
- 專案管理中最常見的10個問題專案管理
- Java面試題講解,Java面試中最容易踩的坑請注意Java面試題
- 容易忽略的URL
- Python學習中最常見的10個列表操作問題Python
- 記Promise一個容易被忽略的特性Promise
- JAVA程式設計題-用java解決兔子問題Java程式設計
- 8個容易被忽略但不能忽略的SD-WAN功能-VecloudCloud
- 針對Java程式設計師的20個Spring MVC訪談問題Java程式設計師SpringMVC
- 遊戲開發中最容易忽略的一環:遊戲音訊你瞭解多少?遊戲開發音訊
- Java程式設計師面試時應注意的三個經典問題!Java程式設計師面試
- 解決問題的能力 > 10倍程式設計師程式設計師
- 優秀的程式設計師10分鐘內能搞定下面5個程式設計問題,你呢?程式設計師
- Java程式設計師,你的簡歷到底問題在哪?Java程式設計師
- Java程式設計師面試常見問題Java程式設計師面試
- 好程式設計師Java培訓分享20個Java程式設計師基礎題程式設計師Java
- Flutter 中的 ListView 的一個容易忽略的知識點FlutterView
- Vue中那些容易被忽略的~Vue
- Java 開發最容易寫的 10 個bugJava
- [併發程式設計]-關於 CAS 的幾個問題程式設計
- 程式碼設計問題
- 面試了一個 5 年 Java 程式設計師,一個問題也不會。。面試Java程式設計師
- 好程式設計師分享:Java面試題常見問題程式設計師Java面試題
- 【譯】程式設計不容易程式設計
- 好程式設計師Java教程分享Java中經常出現的問題程式設計師Java
- Laravel 訪問器 $appends 忽略的問題LaravelAPP
- 一個引發程式設計師們幹架的問題程式設計師
- Java程式設計師必備的10個大資料框架!Java程式設計師大資料框架
- 小白程式設計師最容易踩的“坑”,你踩過幾個?程式設計師
- 解決java網路程式設計IPv6問題Java程式設計
- 程式設計師簡歷中最致命的「八個錯誤 」及解決方法程式設計師
- Java 開發者最容易犯的10個錯誤Java