<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # [Java編程常見問題匯總(轉載)](https://www.cnblogs.com/Qian123/articles/5664924.html) **閱讀目錄** * [字符串連接誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label0) * [錯誤的使用StringBuffer](https://www.cnblogs.com/Qian123/articles/5664924.html#_label1) * [測試字符串相等性](https://www.cnblogs.com/Qian123/articles/5664924.html#_label2) * [數字轉換成字符串](https://www.cnblogs.com/Qian123/articles/5664924.html#_label3) * [利用不可變對象(Immutable)](https://www.cnblogs.com/Qian123/articles/5664924.html#_label4) * [請使用XML解析器](https://www.cnblogs.com/Qian123/articles/5664924.html#_label5) * [請使用JDom組裝XML](https://www.cnblogs.com/Qian123/articles/5664924.html#_label6) * [XML編碼陷阱](https://www.cnblogs.com/Qian123/articles/5664924.html#_label7) * [未指定字符編碼](https://www.cnblogs.com/Qian123/articles/5664924.html#_label8) * [未對數據流進行緩存](https://www.cnblogs.com/Qian123/articles/5664924.html#_label9) * [無限使用heap內存](https://www.cnblogs.com/Qian123/articles/5664924.html#_label10) * [不指定超時時間](https://www.cnblogs.com/Qian123/articles/5664924.html#_label11) * [頻繁使用計時器](https://www.cnblogs.com/Qian123/articles/5664924.html#_label12) * [捕獲所有的異常](https://www.cnblogs.com/Qian123/articles/5664924.html#_label13) * [忽略所有異常](https://www.cnblogs.com/Qian123/articles/5664924.html#_label14) * [重復包裝RuntimeException](https://www.cnblogs.com/Qian123/articles/5664924.html#_label15) * [不正確的傳播異常](https://www.cnblogs.com/Qian123/articles/5664924.html#_label16) * [用日志記錄異常](https://www.cnblogs.com/Qian123/articles/5664924.html#_label17) * [異常處理不徹底](https://www.cnblogs.com/Qian123/articles/5664924.html#_label18) * [捕獲不可能出現的異常](https://www.cnblogs.com/Qian123/articles/5664924.html#_label19) * [transient的誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label20) * [不必要的初始化](https://www.cnblogs.com/Qian123/articles/5664924.html#_label21) * [最好用靜態final定義Log變量](https://www.cnblogs.com/Qian123/articles/5664924.html#_label22) * [選擇錯誤的類加載器](https://www.cnblogs.com/Qian123/articles/5664924.html#_label23) * [反射使用不當](https://www.cnblogs.com/Qian123/articles/5664924.html#_label24) * [不必要的同步](https://www.cnblogs.com/Qian123/articles/5664924.html#_label25) * [錯誤的選擇List類型](https://www.cnblogs.com/Qian123/articles/5664924.html#_label26) * [HashMap size陷阱](https://www.cnblogs.com/Qian123/articles/5664924.html#_label27) * [對Hashtable, HashMap 和 HashSet了解不夠](https://www.cnblogs.com/Qian123/articles/5664924.html#_label28) * [對List的誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label29) * [用數組來描述一個結構](https://www.cnblogs.com/Qian123/articles/5664924.html#_label30) * [對方法過度限制](https://www.cnblogs.com/Qian123/articles/5664924.html#_label31) * [對POJO的setter方法畫蛇添足](https://www.cnblogs.com/Qian123/articles/5664924.html#_label32) * [日歷對象(Calendar)誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label33) * [TimeZone的誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label34) * [時區(Time Zone)調整的誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label35) * [Calendar.getInstance()的誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label36) * [Date.setTime()的誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label37) * [SimpleDateFormat非線程安全誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label38) * [使用全局參數配置常量類/接口](https://www.cnblogs.com/Qian123/articles/5664924.html#_label39) * [忽略造型溢出(cast overflow)](https://www.cnblogs.com/Qian123/articles/5664924.html#_label40) * [對float和double使用==操作](https://www.cnblogs.com/Qian123/articles/5664924.html#_label41) * [用浮點數來保存money](https://www.cnblogs.com/Qian123/articles/5664924.html#_label42) * [不使用finally塊釋放資源](https://www.cnblogs.com/Qian123/articles/5664924.html#_label43) * [finalize方法誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label44) * [Thread.interrupted方法誤用](https://www.cnblogs.com/Qian123/articles/5664924.html#_label45) * [在靜態變量初始化時創建線程](https://www.cnblogs.com/Qian123/articles/5664924.html#_label46) * [已取消的定時器任務依然持有狀態](https://www.cnblogs.com/Qian123/articles/5664924.html#_label47) 轉載于[http://www.codeceo.com/article/java-programming-tips.html](http://www.codeceo.com/article/java-programming-tips.html) 每天在寫Java程序,其實里面有一些細節大家可能沒怎么注意,這不,有人總結了一個我們編程中常見的問題。雖然一般沒有什么大問題,但是最好別這樣做。另外這里提到的很多問題其實可以通過Findbugs(?http://findbugs.sourceforge.net/?)來幫我們進行檢查出來。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 字符串連接誤用 錯誤的寫法: String s = "";&nbsp;for&nbsp;(Person p : persons) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s += ", "&nbsp;+ p.getName();&nbsp;}&nbsp;s = s.substring(2); //remove first comma 正確的寫法: ~~~ StringBuilder sb = new StringBuilder(persons.size() * 16); // well estimated buffer for (Person p : persons) { if (sb.length() > 0) sb.append(", "); sb.append(p.getName); } ~~~ [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 錯誤的使用StringBuffer 錯誤的寫法: StringBuffer sb = new&nbsp;StringBuffer();&nbsp;sb.append("Name: ");&nbsp;sb.append(name + '\n');&nbsp;sb.append("!");&nbsp;...&nbsp;String s = sb.toString(); 問題在第三行,append char比String性能要好,另外就是初始化StringBuffer沒有指定size,導致中間append時可能重新調整內部數組大小。如果是JDK1.5最好用StringBuilder取代StringBuffer,除非有線程安全的要求。還有一種方式就是可以直接連接字符串。缺點就是無法初始化時指定長度。 正確的寫法: StringBuilder sb = new&nbsp;StringBuilder(100);&nbsp;sb.append("Name: ");&nbsp;sb.append(name);&nbsp;sb.append("\n!");&nbsp;String s = sb.toString(); 或者這樣寫: String s = "Name: "&nbsp;+ name + "\n!"; [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 測試字符串相等性 錯誤的寫法: if&nbsp;(name.compareTo("John") == 0) ...&nbsp;if&nbsp;(name == "John") ...&nbsp;if&nbsp;(name.equals("John")) ...&nbsp;if&nbsp;("".equals(name)) ... 上面的代碼沒有錯,但是不夠好。compareTo不夠簡潔,==原義是比較兩個對象是否一樣。另外比較字符是否為空,最好判斷它的長度。 正確的寫法: if&nbsp;("John".equals(name)) ...&nbsp;if&nbsp;(name.length() == 0) ...&nbsp;if&nbsp;(name.isEmpty()) ... [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 數字轉換成字符串 錯誤的寫法: ""&nbsp;+ set.size()&nbsp;new&nbsp;Integer(set.size()).toString() 正確的寫法: String.valueOf(set.size()) [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 利用不可變對象(Immutable) 錯誤的寫法: zero = new&nbsp;Integer(0);&nbsp;return&nbsp;Boolean.valueOf("true"); 正確的寫法: zero = Integer.valueOf(0);&nbsp;return&nbsp;Boolean.TRUE; [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 請使用XML解析器 錯誤的寫法: int&nbsp;start = xml.indexOf("&lt;name&gt;") + "&lt;name&gt;".length();&nbsp;int&nbsp;end = xml.indexOf("&lt;/name&gt;");&nbsp;String name = xml.substring(start, end); 正確的寫法: SAXBuilder builder = new&nbsp;SAXBuilder(false);&nbsp;Document doc = doc = builder.build(new&nbsp;StringReader(xml));&nbsp;String name = doc.getRootElement().getChild("name").getText(); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 請使用JDom組裝XML 錯誤的寫法: String name = ...&nbsp;String attribute = ...&nbsp;String xml = "&lt;root&gt;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+"&lt;name att=\""+ attribute +"\"&gt;"+ name +"&lt;/name&gt;"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+"&lt;/root&gt;"; 正確的寫法: Element root = new&nbsp;Element("root");&nbsp;root.setAttribute("att", attribute);&nbsp;root.setText(name);&nbsp;Document doc = new&nbsp;Documet();&nbsp;doc.setRootElement(root);&nbsp;XmlOutputter out = new&nbsp;XmlOutputter(Format.getPrettyFormat());&nbsp;String xml = out.outputString(root); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## XML編碼陷阱 錯誤的寫法: String xml = FileUtils.readTextFile("my.xml"); 因為xml的編碼在文件中指定的,而在讀文件的時候必須指定編碼。另外一個問題不能一次就將一個xml文件用String保存,這樣對內存會造成不必要的浪費,正確的做法用InputStream來邊讀取邊處理。為了解決編碼的問題, 最好使用XML解析器來處理。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 未指定字符編碼 錯誤的寫法: Reader r = new&nbsp;FileReader(file);&nbsp;Writer w = new&nbsp;FileWriter(file);&nbsp;Reader r = new&nbsp;InputStreamReader(inputStream);&nbsp;Writer w = new&nbsp;OutputStreamWriter(outputStream);&nbsp;String s = new&nbsp;String(byteArray); // byteArray is a byte[]&nbsp;byte[] a = string.getBytes(); 這樣的代碼主要不具有跨平臺可移植性。因為不同的平臺可能使用的是不同的默認字符編碼。 正確的寫法: Reader r = new&nbsp;InputStreamReader(new&nbsp;FileInputStream(file), "ISO-8859-1");&nbsp;Writer w = new&nbsp;OutputStreamWriter(new&nbsp;FileOutputStream(file), "ISO-8859-1");&nbsp;Reader r = new&nbsp;InputStreamReader(inputStream, "UTF-8");&nbsp;Writer w = new&nbsp;OutputStreamWriter(outputStream, "UTF-8");&nbsp;String s = new&nbsp;String(byteArray, "ASCII");&nbsp;byte[] a = string.getBytes("ASCII"); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 未對數據流進行緩存 錯誤的寫法: InputStream in = new&nbsp;FileInputStream(file);&nbsp;&nbsp;int&nbsp;b;&nbsp;&nbsp;while&nbsp;((b = in.read()) != -1) {&nbsp;&nbsp;...&nbsp;&nbsp;} 上面的代碼是一個byte一個byte的讀取,導致頻繁的本地JNI文件系統訪問,非常低效,因為調用本地方法是非常耗時的。最好用BufferedInputStream包裝一下。曾經做過一個測試,從/dev/zero下讀取1MB,大概花了1s,而用BufferedInputStream包裝之后只需要60ms,性能提高了94%! 這個也適用于output stream操作以及socket操作。 正確的寫法: InputStream in = new&nbsp;BufferedInputStream(new&nbsp;FileInputStream(file)); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 無限使用heap內存 錯誤的寫法: byte[] pdf = toPdf(file); 這里有一個前提,就是文件大小不能講JVM的heap撐爆。否則就等著OOM吧,尤其是在高并發的服務器端代碼。最好的做法是采用Stream的方式邊讀取邊存儲(本地文件或database)。 正確的寫法: File pdf = toPdf(file); 另外,對于服務器端代碼來說,為了系統的安全,至少需要對文件的大小進行限制。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 不指定超時時間 錯誤的代碼: Socket socket = ...&nbsp;&nbsp;socket.connect(remote);&nbsp;&nbsp;InputStream in = socket.getInputStream();&nbsp;&nbsp;int&nbsp;i = in.read(); 這種情況在工作中已經碰到不止一次了。個人經驗一般超時不要超過20s。這里有一個問題,connect可以指定超時時間,但是read無法指定超時時間。但是可以設置阻塞(block)時間。 正確的寫法: Socket socket = ...&nbsp;&nbsp;socket.connect(remote, 20000); // fail after 20s&nbsp;&nbsp;InputStream in = socket.getInputStream();&nbsp;&nbsp;socket.setSoTimeout(15000);&nbsp;&nbsp;int&nbsp;i = in.read(); 另外,文件的讀取(FileInputStream, FileChannel, FileDescriptor, File)沒法指定超時時間, 而且IO操作均涉及到本地方法調用, 這個更操作了JVM的控制范圍,在分布式文件系統中,對IO的操作內部實際上是網絡調用。一般情況下操作60s的操作都可以認為已經超時了。為了解決這些問題,一般采用緩存和異步/消息隊列處理。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 頻繁使用計時器 錯誤代碼: for&nbsp;(...) {&nbsp;&nbsp;long&nbsp;t = System.currentTimeMillis();&nbsp;&nbsp;long&nbsp;t = System.nanoTime();&nbsp;&nbsp;Date d = new&nbsp;Date();&nbsp;&nbsp;Calendar c = new&nbsp;GregorianCalendar();&nbsp;&nbsp;} 每次new一個Date或Calendar都會涉及一次本地調用來獲取當前時間(盡管這個本地調用相對其他本地方法調用要快)。 如果對時間不是特別敏感,這里使用了clone方法來新建一個Date實例。這樣相對直接new要高效一些。 正確的寫法: Date d = new&nbsp;Date();&nbsp;&nbsp;for&nbsp;(E entity : entities) {&nbsp;&nbsp;entity.doSomething();&nbsp;&nbsp;entity.setUpdated((Date) d.clone());&nbsp;&nbsp;} 如果循環操作耗時較長(超過幾ms),那么可以采用下面的方法,立即創建一個Timer,然后定期根據當前時間更新時間戳,在我的系統上比直接new一個時間對象快200倍: private&nbsp;volatile&nbsp;long&nbsp;time;&nbsp;&nbsp;Timer timer = new&nbsp;Timer(true);&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;time = System.currentTimeMillis();&nbsp;&nbsp;timer.scheduleAtFixedRate(new&nbsp;TimerTask() {&nbsp;&nbsp;public&nbsp;void&nbsp;run() {&nbsp;&nbsp;time = System.currentTimeMillis();&nbsp;&nbsp;}&nbsp;&nbsp;}, 0L, 10L); // granularity 10ms&nbsp;&nbsp;for&nbsp;(E entity : entities) {&nbsp;&nbsp;entity.doSomething();&nbsp;&nbsp;entity.setUpdated(new&nbsp;Date(time));&nbsp;&nbsp;}&nbsp;&nbsp;} finally&nbsp;{&nbsp;&nbsp;timer.cancel();&nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 捕獲所有的異常 錯誤的寫法: Query q = ...&nbsp;&nbsp;Person p;&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;p = (Person) q.getSingleResult();&nbsp;&nbsp;} catch(Exception e) {&nbsp;&nbsp;p = null;&nbsp;&nbsp;} 這是EJB3的一個查詢操作,可能出現異常的原因是:結果不唯一;沒有結果;數據庫無法訪問,而捕獲所有的異常,設置為null將掩蓋各種異常情況。 正確的寫法: Query q = ...&nbsp;&nbsp;Person p;&nbsp;&nbsp;try&nbsp;{&nbsp;&nbsp;p = (Person) q.getSingleResult();&nbsp;&nbsp;} catch(NoResultException e) {&nbsp;&nbsp;p = null;&nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 忽略所有異常 錯誤的寫法: try&nbsp;{&nbsp;&nbsp;doStuff();&nbsp;&nbsp;} catch(Exception e) {&nbsp;&nbsp;log.fatal("Could not do stuff");&nbsp;&nbsp;}&nbsp;&nbsp;doMoreStuff(); 這個代碼有兩個問題, 一個是沒有告訴調用者, 系統調用出錯了. 第二個是日志沒有出錯原因, 很難跟蹤定位問題。 正確的寫法: try&nbsp;{&nbsp;&nbsp;doStuff();&nbsp;&nbsp;} catch(Exception e) {&nbsp;&nbsp;throw&nbsp;new&nbsp;MyRuntimeException("Could not do stuff because: "+ e.getMessage, e);&nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 重復包裝RuntimeException 錯誤的寫法: try&nbsp;{ &nbsp;&nbsp;doStuff(); &nbsp;&nbsp;}&nbsp;catch(Exception&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e); &nbsp;&nbsp;} 正確的寫法: try&nbsp;{ &nbsp;&nbsp;doStuff(); &nbsp;&nbsp;}&nbsp;catch(RuntimeException&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;e; &nbsp;&nbsp;}&nbsp;catch(Exception&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.getMessage(),&nbsp;e); &nbsp;&nbsp;} &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;doStuff(); &nbsp;&nbsp;}&nbsp;catch(IOException&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.getMessage(),&nbsp;e); &nbsp;&nbsp;}&nbsp;catch(NamingException&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.getMessage(),&nbsp;e); &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 不正確的傳播異常 錯誤的寫法: try&nbsp;{ &nbsp;&nbsp;}&nbsp;catch(ParseException&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(); &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.toString()); &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.getMessage()); &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e); &nbsp;&nbsp;} 主要是沒有正確的將內部的錯誤信息傳遞給調用者. 第一個完全丟掉了內部錯誤信息, 第二個錯誤信息依賴toString方法, 如果沒有包含最終的嵌套錯誤信息, 也會出現丟失, 而且可讀性差. 第三個稍微好一些, 第四個跟第二個一樣。 正確的寫法: try&nbsp;{ &nbsp;&nbsp;}&nbsp;catch(ParseException&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.getMessage(),&nbsp;e); &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 用日志記錄異常 錯誤的寫法: try&nbsp;{ &nbsp;&nbsp;... &nbsp;&nbsp;}&nbsp;catch(ExceptionA&nbsp;e)&nbsp;{ &nbsp;&nbsp;log.error(e.getMessage(),&nbsp;e); &nbsp;&nbsp;throw&nbsp;e; &nbsp;&nbsp;}&nbsp;catch(ExceptionB&nbsp;e)&nbsp;{ &nbsp;&nbsp;log.error(e.getMessage(),&nbsp;e); &nbsp;&nbsp;throw&nbsp;e; &nbsp;&nbsp;} 一般情況下在日志中記錄異常是不必要的, 除非調用方沒有記錄日志。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 異常處理不徹底 錯誤的寫法: try&nbsp;{ &nbsp;&nbsp;is&nbsp;=&nbsp;new&nbsp;FileInputStream(inFile); &nbsp;&nbsp;os&nbsp;=&nbsp;new&nbsp;FileOutputStream(outFile); &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;is.close(); &nbsp;&nbsp;os.close(); &nbsp;&nbsp;}&nbsp;catch(IOException&nbsp;e)&nbsp;{ &nbsp;&nbsp;/*&nbsp;we&nbsp;can't&nbsp;do&nbsp;anything&nbsp;*/&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;} is可能close失敗, 導致os沒有close 正確的寫法: try&nbsp;{ &nbsp;&nbsp;is&nbsp;=&nbsp;new&nbsp;FileInputStream(inFile); &nbsp;&nbsp;os&nbsp;=&nbsp;new&nbsp;FileOutputStream(outFile); &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;try&nbsp;{&nbsp;if&nbsp;(is&nbsp;!=&nbsp;null)&nbsp;is.close();&nbsp;}&nbsp;catch(IOException&nbsp;e)&nbsp;{/*&nbsp;we&nbsp;can't&nbsp;do&nbsp;anything&nbsp;*/} &nbsp;&nbsp;try&nbsp;{&nbsp;if&nbsp;(os&nbsp;!=&nbsp;null)&nbsp;os.close();&nbsp;}&nbsp;catch(IOException&nbsp;e)&nbsp;{/*&nbsp;we&nbsp;can't&nbsp;do&nbsp;anything&nbsp;*/} &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 捕獲不可能出現的異常 錯誤的寫法: try&nbsp;{ &nbsp;&nbsp;...&nbsp;do&nbsp;risky&nbsp;stuff&nbsp;... &nbsp;&nbsp;}&nbsp;catch(SomeException&nbsp;e)&nbsp;{ &nbsp;&nbsp;//&nbsp;never&nbsp;happens &nbsp;&nbsp;} &nbsp;&nbsp;...&nbsp;do&nbsp;some&nbsp;more&nbsp;... 正確的寫法: try&nbsp;{ &nbsp;&nbsp;...&nbsp;do&nbsp;risky&nbsp;stuff&nbsp;... &nbsp;&nbsp;}&nbsp;catch(SomeException&nbsp;e)&nbsp;{ &nbsp;&nbsp;//&nbsp;never&nbsp;happens&nbsp;hopefully &nbsp;&nbsp;throw&nbsp;new&nbsp;IllegalStateException(e.getMessage(),&nbsp;e);&nbsp;//&nbsp;crash&nbsp;early,&nbsp;passing&nbsp;all&nbsp;information &nbsp;&nbsp;} &nbsp;&nbsp;...&nbsp;do&nbsp;some&nbsp;more&nbsp;... [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## transient的誤用 錯誤的寫法: public&nbsp;class&nbsp;A&nbsp;implements&nbsp;Serializable&nbsp;{ &nbsp;&nbsp;private&nbsp;String&nbsp;someState; &nbsp;&nbsp;private&nbsp;transient&nbsp;Log&nbsp;log&nbsp;=&nbsp;LogFactory.getLog(getClass()); &nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;f()&nbsp;{ &nbsp;&nbsp;log.debug("enter&nbsp;f"); &nbsp;&nbsp;... &nbsp;&nbsp;} &nbsp;&nbsp;} 這里的本意是不希望Log對象被序列化. 不過這里在反序列化時, 會因為log未初始化, 導致f()方法拋空指針, 正確的做法是將log定義為靜態變量或者定位為具備變量。 正確的寫法: public&nbsp;class&nbsp;A&nbsp;implements&nbsp;Serializable&nbsp;{ &nbsp;&nbsp;private&nbsp;String&nbsp;someState; &nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;Log&nbsp;log&nbsp;=&nbsp;LogFactory.getLog(A.class); &nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;f()&nbsp;{ &nbsp;&nbsp;log.debug("enter&nbsp;f"); &nbsp;&nbsp;... &nbsp;&nbsp;} &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;class&nbsp;A&nbsp;implements&nbsp;Serializable&nbsp;{ &nbsp;&nbsp;private&nbsp;String&nbsp;someState; &nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;f()&nbsp;{ &nbsp;&nbsp;Log&nbsp;log&nbsp;=&nbsp;LogFactory.getLog(getClass()); &nbsp;&nbsp;log.debug("enter&nbsp;f"); &nbsp;&nbsp;... &nbsp;&nbsp;} &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 不必要的初始化 錯誤的寫法: public&nbsp;class&nbsp;B&nbsp;{ &nbsp;&nbsp;private&nbsp;int&nbsp;count&nbsp;=&nbsp;0; &nbsp;&nbsp;private&nbsp;String&nbsp;name&nbsp;=&nbsp;null; &nbsp;&nbsp;private&nbsp;boolean&nbsp;important&nbsp;=&nbsp;false; &nbsp;&nbsp;} 這里的變量會在初始化時使用默認值:0, null, false, 因此上面的寫法有些多此一舉。 正確的寫法: public&nbsp;class&nbsp;B&nbsp;{ &nbsp;&nbsp;private&nbsp;int&nbsp;count; &nbsp;&nbsp;private&nbsp;String&nbsp;name; &nbsp;&nbsp;private&nbsp;boolean&nbsp;important; &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 最好用靜態final定義Log變量 private&nbsp;static&nbsp;final&nbsp;Log&nbsp;log&nbsp;=&nbsp;LogFactory.getLog(MyClass.class); 這樣做的好處有三: * 可以保證線程安全 * 靜態或非靜態代碼都可用 * 不會影響對象序列化 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 選擇錯誤的類加載器 錯誤的代碼: Class&nbsp;clazz&nbsp;=&nbsp;Class.forName(name); &nbsp;&nbsp;Class&nbsp;clazz&nbsp;=&nbsp;getClass().getClassLoader().loadClass(name); 這里本意是希望用當前類來加載希望的對象, 但是這里的getClass()可能拋出異常, 特別在一些受管理的環境中, 比如應用服務器, web容器, Java WebStart環境中, 最好的做法是使用當前應用上下文的類加載器來加載。 正確的寫法: ClassLoader&nbsp;cl&nbsp;=&nbsp;Thread.currentThread().getContextClassLoader(); &nbsp;&nbsp;if&nbsp;(cl&nbsp;==&nbsp;null)&nbsp;cl&nbsp;=&nbsp;MyClass.class.getClassLoader();&nbsp;//&nbsp;fallback &nbsp;&nbsp;Class&nbsp;clazz&nbsp;=&nbsp;cl.loadClass(name); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 反射使用不當 錯誤的寫法: Class&nbsp;beanClass&nbsp;=&nbsp;... &nbsp;&nbsp;if&nbsp;(beanClass.newInstance()&nbsp;instanceof&nbsp;TestBean)&nbsp;... 這里的本意是檢查beanClass是否是TestBean或是其子類, 但是創建一個類實例可能沒那么簡單, 首先實例化一個對象會帶來一定的消耗, 另外有可能類沒有定義默認構造函數. 正確的做法是用Class.isAssignableFrom(Class) 方法。 正確的寫法: Class&nbsp;beanClass&nbsp;=&nbsp;... &nbsp;&nbsp;if&nbsp;(TestBean.class.isAssignableFrom(beanClass))&nbsp;... [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 不必要的同步 錯誤的寫法: Collection&nbsp;l&nbsp;=&nbsp;new&nbsp;Vector(); &nbsp;&nbsp;for&nbsp;(...)&nbsp;{ &nbsp;&nbsp;l.add(object); &nbsp;&nbsp;} Vector是ArrayList同步版本。 正確的寫法: Collection&nbsp;l&nbsp;=&nbsp;new&nbsp;ArrayList(); &nbsp;&nbsp;for&nbsp;(...)&nbsp;{ &nbsp;&nbsp;l.add(object); &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 錯誤的選擇List類型 根據下面的表格數據來進行選擇 &nbsp;ArrayListLinkedListadd (append)O(1) or ~O(log(n)) if growingO(1)insert (middle)O(n) or ~O(n*log(n)) if growingO(n)remove (middle)O(n) (always performs complete copy)O(n)iterateO(n)O(n)get by indexO(1)O(n) [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## HashMap size陷阱 錯誤的寫法: Map&nbsp;map&nbsp;=&nbsp;new&nbsp;HashMap(collection.size()); &nbsp;for&nbsp;(Object&nbsp;o&nbsp;:&nbsp;collection)&nbsp;{ &nbsp;&nbsp;&nbsp;map.put(o.key,&nbsp;o.value); &nbsp;} 這里可以參考guava的Maps.newHashMapWithExpectedSize的實現. 用戶的本意是希望給HashMap設置初始值, 避免擴容(resize)的開銷. 但是沒有考慮當添加的元素數量達到HashMap容量的75%時將出現resize。 正確的寫法: Map&nbsp;map&nbsp;=&nbsp;new&nbsp;HashMap(1&nbsp;+&nbsp;(int)&nbsp;(collection.size()&nbsp;/&nbsp;0.75)); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 對Hashtable, HashMap 和 HashSet了解不夠 這里主要需要了解HashMap和Hashtable的內部實現上, 它們都使用Entry包裝來封裝key/value, Entry內部除了要保存Key/Value的引用, 還需要保存hash桶中next Entry的應用, 因此對內存會有不小的開銷, 而HashSet內部實現其實就是一個HashMap. 有時候IdentityHashMap可以作為一個不錯的替代方案. 它在內存使用上更有效(沒有用Entry封裝, 內部采用Object\[\]). 不過需要小心使用. 它的實現違背了Map接口的定義. 有時候也可以用ArrayList來替換HashSet. 這一切的根源都是由于JDK內部沒有提供一套高效的Map和Set實現。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 對List的誤用 建議下列場景用Array來替代List: * list長度固定,比如一周中的每一天 * 對list頻繁的遍歷,比如超過1w次 * 需要對數字進行包裝(主要JDK沒有提供基本類型的List) 比如下面的代碼。 錯誤的寫法: List&lt;Integer&gt;&nbsp;codes&nbsp;=&nbsp;new&nbsp;ArrayList&lt;Integer&gt;(); &nbsp;codes.add(Integer.valueOf(10)); &nbsp;codes.add(Integer.valueOf(20)); &nbsp;codes.add(Integer.valueOf(30)); &nbsp;codes.add(Integer.valueOf(40)); 正確的寫法: int[]&nbsp;codes&nbsp;=&nbsp;{&nbsp;10,&nbsp;20,&nbsp;30,&nbsp;40&nbsp;}; 錯誤的寫法: //&nbsp;horribly&nbsp;slow&nbsp;and&nbsp;a&nbsp;memory&nbsp;waster&nbsp;if&nbsp;l&nbsp;has&nbsp;a&nbsp;few&nbsp;thousand&nbsp;elements&nbsp;(try&nbsp;it&nbsp;yourself!) &nbsp;List&lt;Mergeable&gt;&nbsp;l&nbsp;=&nbsp;...; &nbsp;for&nbsp;(int&nbsp;i=0;&nbsp;i&nbsp;&lt;&nbsp;l.size()-1;&nbsp;i++)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp; Mergeable&nbsp;one&nbsp;=&nbsp;l.get(i); &nbsp;&nbsp;&nbsp;&nbsp; Iterator&lt;Mergeable&gt;&nbsp;j&nbsp;=&nbsp;l.iterator(i+1);&nbsp;//&nbsp;memory&nbsp;allocation! &nbsp;&nbsp;&nbsp;&nbsp; while&nbsp;(j.hasNext())&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; Mergeable&nbsp;other&nbsp;=&nbsp;l.next(); &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; if&nbsp;(one.canMergeWith(other))&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; one.merge(other); &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; other.remove(); &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; } &nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;} 正確的寫法: //&nbsp;quite&nbsp;fast&nbsp;and&nbsp;no&nbsp;memory&nbsp;allocation &nbsp;Mergeable[]&nbsp;l&nbsp;=&nbsp;...; &nbsp;for&nbsp;(int&nbsp;i=0;&nbsp;i&nbsp;&lt;&nbsp;l.length-1;&nbsp;i++)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp; Mergeable&nbsp;one&nbsp;=&nbsp;l[i]; &nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;(int&nbsp;j=i+1;&nbsp;j&nbsp;&lt;&nbsp;l.length;&nbsp;j++)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; Mergeable&nbsp;other&nbsp;=&nbsp;l[j]; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; if&nbsp;(one.canMergeWith(other))&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; one.merge(other); &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l[j]&nbsp;=&nbsp;null; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; } &nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;} 實際上Sun也意識到這一點, 因此在JDK中, Collections.sort()就是將一個List拷貝到一個數組中然后調用Arrays.sort方法來執行排序。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 用數組來描述一個結構 錯誤用法: /** &nbsp;&nbsp;*&nbsp;@returns&nbsp;[1]:&nbsp;Location,&nbsp;[2]:&nbsp;Customer,&nbsp;[3]:&nbsp;Incident &nbsp;&nbsp;*/&nbsp;&nbsp;&nbsp;Object[]&nbsp;getDetails(int&nbsp;id)&nbsp;{... 這里用數組+文檔的方式來描述一個方法的返回值. 雖然很簡單, 但是很容易誤用, 正確的做法應該是定義個類。 正確的寫法: Details&nbsp;getDetails(int&nbsp;id)&nbsp;{...} &nbsp;&nbsp;private&nbsp;class&nbsp;Details&nbsp;{ &nbsp;&nbsp;public&nbsp;Location&nbsp;location; &nbsp;&nbsp;public&nbsp;Customer&nbsp;customer; &nbsp;&nbsp;public&nbsp;Incident&nbsp;incident; &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 對方法過度限制 錯誤用法: public&nbsp;void&nbsp;notify(Person&nbsp;p)&nbsp;{ &nbsp;&nbsp;... &nbsp;&nbsp;sendMail(p.getName(),&nbsp;p.getFirstName(),&nbsp;p.getEmail()); &nbsp;&nbsp;... &nbsp;&nbsp;} &nbsp;&nbsp;class&nbsp;PhoneBook&nbsp;{ &nbsp;&nbsp;String&nbsp;lookup(String&nbsp;employeeId)&nbsp;{ &nbsp;&nbsp;Employee&nbsp;emp&nbsp;=&nbsp;... &nbsp;&nbsp;return&nbsp;emp.getPhone(); &nbsp;&nbsp;} &nbsp;&nbsp;} 第一個例子是對方法參數做了過多的限制, 第二個例子對方法的返回值做了太多的限制。 正確的寫法: public&nbsp;void&nbsp;notify(Person&nbsp;p)&nbsp;{ &nbsp;&nbsp;... &nbsp;&nbsp;sendMail(p); &nbsp;&nbsp;... &nbsp;&nbsp;} &nbsp;&nbsp;class&nbsp;EmployeeDirectory&nbsp;{ &nbsp;&nbsp;Employee&nbsp;lookup(String&nbsp;employeeId)&nbsp;{ &nbsp;&nbsp;Employee&nbsp;emp&nbsp;=&nbsp;... &nbsp;&nbsp;return&nbsp;emp; &nbsp;&nbsp;} &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 對POJO的setter方法畫蛇添足 錯誤的寫法: private&nbsp;String&nbsp;name; &nbsp;&nbsp;public&nbsp;void&nbsp;setName(String&nbsp;name)&nbsp;{ &nbsp;&nbsp;this.name&nbsp;=&nbsp;name.trim(); &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;void&nbsp;String&nbsp;getName()&nbsp;{ &nbsp;&nbsp;return&nbsp;this.name; &nbsp;&nbsp;} 有時候我們很討厭字符串首尾出現空格, 所以在setter方法中進行了trim處理, 但是這樣做的結果帶來的副作用會使getter方法的返回值和setter方法不一致, 如果只是將JavaBean當做一個數據容器, 那么最好不要包含任何業務邏輯. 而將業務邏輯放到專門的業務層或者控制層中處理。 正確的做法: person.setName(textInput.getText().trim()); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 日歷對象(Calendar)誤用 錯誤的寫法: Calendar&nbsp;cal&nbsp;=&nbsp;new&nbsp;GregorianCalender(TimeZone.getTimeZone("Europe/Zurich")); &nbsp;&nbsp;cal.setTime(date); &nbsp;&nbsp;cal.add(Calendar.HOUR_OF_DAY,&nbsp;8); &nbsp;&nbsp;date&nbsp;=&nbsp;cal.getTime(); 這里主要是對date, time, calendar和time zone不了解導致. 而在一個時間上增加8小時, 跟time zone沒有任何關系, 所以沒有必要使用Calendar, 直接用Date對象即可, 而如果是增加天數的話, 則需要使用Calendar, 因為采用不同的時令制可能一天的小時數是不同的(比如有些DST是23或者25個小時) 正確的寫法: date&nbsp;=&nbsp;new&nbsp;Date(date.getTime()&nbsp;+&nbsp;8L&nbsp;*&nbsp;3600L&nbsp;*&nbsp;1000L);&nbsp;//&nbsp;add&nbsp;8&nbsp;hrs [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## TimeZone的誤用 錯誤的寫法: Calendar&nbsp;cal&nbsp;=&nbsp;new&nbsp;GregorianCalendar(); &nbsp;&nbsp;cal.setTime(date); &nbsp;&nbsp;cal.set(Calendar.HOUR_OF_DAY,&nbsp;0); &nbsp;&nbsp;cal.set(Calendar.MINUTE,&nbsp;0); &nbsp;&nbsp;cal.set(Calendar.SECOND,&nbsp;0); &nbsp;&nbsp;Date&nbsp;startOfDay&nbsp;=&nbsp;cal.getTime(); 這里有兩個錯誤, 一個是沒有沒有將毫秒歸零, 不過最大的錯誤是沒有指定TimeZone, 不過一般的桌面應用沒有問題, 但是如果是服務器端應用則會有一些問題, 比如同一時刻在上海和倫敦就不一樣, 因此需要指定的TimeZone. 正確的寫法: Calendar&nbsp;cal&nbsp;=&nbsp;new&nbsp;GregorianCalendar(user.getTimeZone()); &nbsp;&nbsp;cal.setTime(date); &nbsp;&nbsp;cal.set(Calendar.HOUR_OF_DAY,&nbsp;0); &nbsp;&nbsp;cal.set(Calendar.MINUTE,&nbsp;0); &nbsp;&nbsp;cal.set(Calendar.SECOND,&nbsp;0); &nbsp;&nbsp;cal.set(Calendar.MILLISECOND,&nbsp;0); &nbsp;&nbsp;Date&nbsp;startOfDay&nbsp;=&nbsp;cal.getTime(); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 時區(Time Zone)調整的誤用 錯誤的寫法: public&nbsp;static&nbsp;Date&nbsp;convertTz(Date&nbsp;date,&nbsp;TimeZone&nbsp;tz)&nbsp;{ &nbsp;&nbsp;Calendar&nbsp;cal&nbsp;=&nbsp;Calendar.getInstance(); &nbsp;&nbsp;cal.setTimeZone(TimeZone.getTimeZone("UTC")); &nbsp;&nbsp;cal.setTime(date); &nbsp;&nbsp;cal.setTimeZone(tz); &nbsp;&nbsp;return&nbsp;cal.getTime(); &nbsp;&nbsp;} 這個方法實際上沒有改變時間, 輸入和輸出是一樣的. 關于時間的問題可以參考這篇文章: http://www.odi.ch/prog/design/datetime.php 這里主要的問題是Date對象并不包含Time Zone信息. 它總是使用UTC(世界統一時間). 而調用Calendar的getTime/setTime方法會自動在當前時區和UTC之間做轉換。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## Calendar.getInstance()的誤用 錯誤的寫法: Calendar&nbsp;c&nbsp;=&nbsp;Calendar.getInstance(); &nbsp;&nbsp;c.set(2009,&nbsp;Calendar.JANUARY,&nbsp;15); Calendar.getInstance()依賴local來選擇一個Calendar實現, 不同實現的2009年是不同的, 比如有些Calendar實現就沒有January月份。 正確的寫法: Calendar&nbsp;c&nbsp;=&nbsp;new&nbsp;GregorianCalendar(timeZone); &nbsp;&nbsp;c.set(2009,&nbsp;Calendar.JANUARY,&nbsp;15); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## Date.setTime()的誤用 錯誤的寫法: account.changePassword(oldPass,&nbsp;newPass); &nbsp;&nbsp;Date&nbsp;lastmod&nbsp;=&nbsp;account.getLastModified(); &nbsp;&nbsp;lastmod.setTime(System.currentTimeMillis()); 在更新密碼之后, 修改一下最后更新時間, 這里的用法沒有錯,但是有更好的做法: 直接傳Date對象. 因為Date是Value Object, 不可變的. 如果更新了Date的值, 實際上是生成一個新的Date實例. 這樣其他地方用到的實際上不在是原來的對象, 這樣可能出現不可預知的異常. 當然這里又涉及到另外一個OO設計的問題, 對外暴露Date實例本身就是不好的做法(一般的做法是在setter方法中設置Date引用參數的clone對象). 另外一種比較好的做法就是直接保存long類型的毫秒數。 正確的做法: account.changePassword(oldPass,&nbsp;newPass); &nbsp;&nbsp;account.setLastModified(new&nbsp;Date()); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## SimpleDateFormat非線程安全誤用 錯誤的寫法: public&nbsp;class&nbsp;Constants&nbsp;{ &nbsp;&nbsp;public&nbsp;static&nbsp;final&nbsp;SimpleDateFormat&nbsp;date&nbsp;=&nbsp;new&nbsp;SimpleDateFormat("dd.MM.yyyy"); &nbsp;&nbsp;} SimpleDateFormat不是線程安全的. 在多線程并行處理的情況下, 會得到非預期的值. 這個錯誤非常普遍! 如果真要在多線程環境下公用同一個SimpleDateFormat, 那么做好做好同步(cache flush, lock contention), 但是這樣會搞得更復雜, 還不如直接new一個實在。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 使用全局參數配置常量類/接口 public&nbsp;interface&nbsp;Constants&nbsp;{ &nbsp;&nbsp;String&nbsp;version&nbsp;=&nbsp;"1.0"; &nbsp;&nbsp;String&nbsp;dateFormat&nbsp;=&nbsp;"dd.MM.yyyy"; &nbsp;&nbsp;String&nbsp;configFile&nbsp;=&nbsp;".apprc"; &nbsp;&nbsp;int&nbsp;maxNameLength&nbsp;=&nbsp;32; &nbsp;&nbsp;String&nbsp;someQuery&nbsp;=&nbsp;"SELECT&nbsp;*&nbsp;FROM&nbsp;..."; &nbsp;&nbsp;} 很多應用都會定義這樣一個全局常量類或接口, 但是為什么這種做法不推薦? 因為這些常量之間基本沒有任何關聯, 只是因為公用才定義在一起. 但是如果其他組件需要使用這些全局變量, 則必須對該常量類產生依賴, 特別是存在server和遠程client調用的場景。 比較好的做法是將這些常量定義在組件內部. 或者局限在一個類庫內部。 [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 忽略造型溢出(cast overflow) 錯誤的寫法: public&nbsp;int&nbsp;getFileSize(File&nbsp;f)&nbsp;{ &nbsp;&nbsp;long&nbsp;l&nbsp;=&nbsp;f.length(); &nbsp;&nbsp;return&nbsp;(int)&nbsp;l; &nbsp;&nbsp;} 這個方法的本意是不支持傳遞超過2GB的文件. 最好的做法是對長度進行檢查, 溢出時拋出異常。 正確的寫法: public&nbsp;int&nbsp;getFileSize(File&nbsp;f)&nbsp;{ &nbsp;&nbsp;long&nbsp;l&nbsp;=&nbsp;f.length(); &nbsp;&nbsp;if&nbsp;(l&nbsp;&gt;&nbsp;Integer.MAX_VALUE)&nbsp;throw&nbsp;new&nbsp;IllegalStateException("int&nbsp;overflow"); &nbsp;&nbsp;return&nbsp;(int)&nbsp;l; &nbsp;&nbsp;} 另一個溢出bug是cast的對象不對, 比如下面第一個println. 正確的應該是下面的那個。 long&nbsp;a&nbsp;=&nbsp;System.currentTimeMillis(); &nbsp;&nbsp;long&nbsp;b&nbsp;=&nbsp;a&nbsp;+&nbsp;100; &nbsp;&nbsp;System.out.println((int)&nbsp;b-a); &nbsp;&nbsp;System.out.println((int)&nbsp;(b-a)); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 對float和double使用==操作 錯誤的寫法: for&nbsp;(float&nbsp;f&nbsp;=&nbsp;10f;&nbsp;f!=0;&nbsp;f-=0.1)&nbsp;{ &nbsp;&nbsp;System.out.println(f); &nbsp;&nbsp;} 上面的[浮點數](http://www.codeceo.com/article/float-number.html "浮點數")遞減只會無限接近0而不會等于0, 這樣會導致上面的for進入死循環. 通常絕不要對float和double使用==操作. 而采用大于和小于操作. 如果java編譯器能針對這種情況給出警告. 或者在java語言規范中不支持浮點數類型的==操作就最好了。 正確的寫法: for&nbsp;(float&nbsp;f&nbsp;=&nbsp;10f;&nbsp;f&gt;0;&nbsp;f-=0.1)&nbsp;{ &nbsp;&nbsp;System.out.println(f); &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 用浮點數來保存money 錯誤的寫法: float&nbsp;total&nbsp;=&nbsp;0.0f; &nbsp;&nbsp;for&nbsp;(OrderLine&nbsp;line&nbsp;:&nbsp;lines)&nbsp;{ &nbsp;&nbsp;total&nbsp;+=&nbsp;line.price&nbsp;*&nbsp;line.count; &nbsp;&nbsp;} &nbsp;&nbsp;double&nbsp;a&nbsp;=&nbsp;1.14&nbsp;*&nbsp;75;&nbsp;//&nbsp;85.5&nbsp;將表示為&nbsp;85.4999... &nbsp;&nbsp;System.out.println(Math.round(a));&nbsp;//&nbsp;輸出值為85 &nbsp;&nbsp;BigDecimal&nbsp;d&nbsp;=&nbsp;new&nbsp;BigDecimal(1.14);&nbsp;//造成精度丟失 這個也是一個老生常談的錯誤. 比如計算100筆訂單, 每筆0.3元, 最終的計算結果是29.9999971. 如果將float類型改為double類型, 得到的結果將是30.000001192092896. 出現這種情況的原因是, 人類和計算的計數方式不同. 人類采用的是十進制, 而計算機是二進制.二進制對于計算機來說非常好使, 但是對于涉及到精確計算的場景就會帶來誤差. 比如銀行金融中的應用。 因此絕不要用浮點類型來保存money數據. 采用浮點數得到的計算結果是不精確的. 即使與int類型做乘法運算也會產生一個不精確的結果.那是因為在用二進制存儲一個浮點數時已經出現了精度丟失. 最好的做法就是用一個string或者固定點數來表示. 為了精確, 這種表示方式需要指定相應的精度值. BigDecimal就滿足了上面所說的需求. 如果在計算的過程中精度的丟失超出了給定的范圍, 將拋出runtime exception. 正確的寫法: BigDecimal&nbsp;total&nbsp;=&nbsp;BigDecimal.ZERO; &nbsp;&nbsp;for&nbsp;(OrderLine&nbsp;line&nbsp;:&nbsp;lines)&nbsp;{ &nbsp;&nbsp;BigDecimal&nbsp;price&nbsp;=&nbsp;new&nbsp;BigDecimal(line.price); &nbsp;&nbsp;BigDecimal&nbsp;count&nbsp;=&nbsp;new&nbsp;BigDecimal(line.count); &nbsp;&nbsp;total&nbsp;=&nbsp;total.add(price.multiply(count));&nbsp;//&nbsp;BigDecimal&nbsp;is&nbsp;immutable! &nbsp;&nbsp;} &nbsp;&nbsp;total&nbsp;=&nbsp;total.setScale(2,&nbsp;RoundingMode.HALF_UP); &nbsp;&nbsp;BigDecimal&nbsp;a&nbsp;=&nbsp;(new&nbsp;BigDecimal("1.14")).multiply(new&nbsp;BigDecimal(75));&nbsp;//&nbsp;85.5&nbsp;exact &nbsp;&nbsp;a&nbsp;=&nbsp;a.setScale(0,&nbsp;RoundingMode.HALF_UP);&nbsp;//&nbsp;86 &nbsp;&nbsp;System.out.println(a);&nbsp;//&nbsp;correct&nbsp;output:&nbsp;86 &nbsp;&nbsp;BigDecimal&nbsp;a&nbsp;=&nbsp;new&nbsp;BigDecimal("1.14"); [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 不使用finally塊釋放資源 錯誤的寫法: public&nbsp;void&nbsp;save(File&nbsp;f)&nbsp;throws&nbsp;IOException&nbsp;{ &nbsp;&nbsp;OutputStream&nbsp;out&nbsp;=&nbsp;new&nbsp;BufferedOutputStream(new&nbsp;FileOutputStream(f)); &nbsp;&nbsp;out.write(...); &nbsp;&nbsp;out.close(); &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;void&nbsp;load(File&nbsp;f)&nbsp;throws&nbsp;IOException&nbsp;{ &nbsp;&nbsp;InputStream&nbsp;in&nbsp;=&nbsp;new&nbsp;BufferedInputStream(new&nbsp;FileInputStream(f)); &nbsp;&nbsp;in.read(...); &nbsp;&nbsp;in.close(); &nbsp;&nbsp;} 上面的代碼打開一個文件輸出流, 操作系統為其分配一個文件句柄, 但是文件句柄是一種非常稀缺的資源, 必須通過調用相應的close方法來被正確的釋放回收. 而為了保證在異常情況下資源依然能被正確回收, 必須將其放在finally block中. 上面的代碼中使用了BufferedInputStream將file stream包裝成了一個buffer stream, 這樣將導致在調用close方法時才會將buffer stream寫入磁盤. 如果在close的時候失敗, 將導致寫入數據不完全. 而對于FileInputStream在finally block的close操作這里將直接忽略。 如果BufferedOutputStream.close()方法執行順利則萬事大吉, 如果失敗這里有一個潛在的bug(http://bugs.sun.com/view\_bug.do?bug\_id=6335274): 在close方法內部調用flush操作的時候, 如果出現異常, 將直接忽略. 因此為了盡量減少數據丟失, 在執行close之前顯式的調用flush操作。 下面的代碼有一個小小的瑕疵: 如果分配file stream成功, 但是分配buffer stream失敗(OOM這種場景), 將導致文件句柄未被正確釋放. 不過這種情況一般不用擔心, 因為JVM的gc將幫助我們做清理。 //&nbsp;code&nbsp;for&nbsp;your&nbsp;cookbook &nbsp;&nbsp;public&nbsp;void&nbsp;save()&nbsp;throws&nbsp;IOException&nbsp;{ &nbsp;&nbsp;File&nbsp;f&nbsp;=&nbsp;... &nbsp;&nbsp;OutputStream&nbsp;out&nbsp;=&nbsp;new&nbsp;BufferedOutputStream(new&nbsp;FileOutputStream(f)); &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;out.write(...); &nbsp;&nbsp;out.flush();&nbsp;//&nbsp;don't&nbsp;lose&nbsp;exception&nbsp;by&nbsp;implicit&nbsp;flush&nbsp;on&nbsp;close &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;out.close(); &nbsp;&nbsp;} &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;void&nbsp;load(File&nbsp;f)&nbsp;throws&nbsp;IOException&nbsp;{ &nbsp;&nbsp;InputStream&nbsp;in&nbsp;=&nbsp;new&nbsp;BufferedInputStream(new&nbsp;FileInputStream(f)); &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;in.read(...); &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;try&nbsp;{&nbsp;in.close();&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;e)&nbsp;{&nbsp;} &nbsp;&nbsp;} &nbsp;&nbsp;} 數據庫訪問也涉及到類似的情況: Car&nbsp;getCar(DataSource&nbsp;ds,&nbsp;String&nbsp;plate)&nbsp;throws&nbsp;SQLException&nbsp;{ &nbsp;&nbsp;Car&nbsp;car&nbsp;=&nbsp;null; &nbsp;&nbsp;Connection&nbsp;c&nbsp;=&nbsp;null; &nbsp;&nbsp;PreparedStatement&nbsp;s&nbsp;=&nbsp;null; &nbsp;&nbsp;ResultSet&nbsp;rs&nbsp;=&nbsp;null; &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;c&nbsp;=&nbsp;ds.getConnection(); &nbsp;&nbsp;s&nbsp;=&nbsp;c.prepareStatement("select&nbsp;make,&nbsp;color&nbsp;from&nbsp;cars&nbsp;where&nbsp;plate=?"); &nbsp;&nbsp;s.setString(1,&nbsp;plate); &nbsp;&nbsp;rs&nbsp;=&nbsp;s.executeQuery(); &nbsp;&nbsp;if&nbsp;(rs.next())&nbsp;{ &nbsp;&nbsp;car&nbsp;=&nbsp;new&nbsp;Car(); &nbsp;&nbsp;car.make&nbsp;=&nbsp;rs.getString(1); &nbsp;&nbsp;car.color&nbsp;=&nbsp;rs.getString(2); &nbsp;&nbsp;} &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;if&nbsp;(rs&nbsp;!=&nbsp;null)&nbsp;try&nbsp;{&nbsp;rs.close();&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{&nbsp;} &nbsp;&nbsp;if&nbsp;(s&nbsp;!=&nbsp;null)&nbsp;try&nbsp;{&nbsp;s.close();&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{&nbsp;} &nbsp;&nbsp;if&nbsp;(c&nbsp;!=&nbsp;null)&nbsp;try&nbsp;{&nbsp;c.close();&nbsp;}&nbsp;catch&nbsp;(SQLException&nbsp;e)&nbsp;{&nbsp;} &nbsp;&nbsp;} &nbsp;&nbsp;return&nbsp;car; &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## finalize方法誤用 錯誤的寫法: public&nbsp;class&nbsp;FileBackedCache&nbsp;{ &nbsp;&nbsp;private&nbsp;File&nbsp;backingStore; &nbsp;&nbsp;&nbsp;... &nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;finalize()&nbsp;throws&nbsp;IOException&nbsp;{ &nbsp;&nbsp;if&nbsp;(backingStore&nbsp;!=&nbsp;null)&nbsp;{ &nbsp;&nbsp;backingStore.close(); &nbsp;&nbsp;backingStore&nbsp;=&nbsp;null; &nbsp;&nbsp;} &nbsp;&nbsp;} &nbsp;&nbsp;} 這個問題Effective Java這本書有詳細的說明. 主要是finalize方法依賴于GC的調用, 其調用時機可能是立馬也可能是幾天以后, 所以是不可預知的. 而JDK的API文檔中對這一點有誤導:建議在該方法中來釋放I/O資源。 正確的做法是定義一個close方法, 然后由外部的容器來負責調用釋放資源。 public&nbsp;class&nbsp;FileBackedCache&nbsp;{ &nbsp;&nbsp;private&nbsp;File&nbsp;backingStore; &nbsp;&nbsp;&nbsp;... &nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;close()&nbsp;throws&nbsp;IOException&nbsp;{ &nbsp;&nbsp;if&nbsp;(backingStore&nbsp;!=&nbsp;null)&nbsp;{ &nbsp;&nbsp;backingStore.close(); &nbsp;&nbsp;backingStore&nbsp;=&nbsp;null; &nbsp;&nbsp;} &nbsp;&nbsp;} &nbsp;&nbsp;} 在JDK 1.7 (Java 7)中已經引入了一個AutoClosable接口. 當變量(不是對象)超出了try-catch的資源使用范圍, 將自動調用close方法。 try&nbsp;(Writer&nbsp;w&nbsp;=&nbsp;new&nbsp;FileWriter(f))&nbsp;{&nbsp;//&nbsp;implements&nbsp;Closable &nbsp;&nbsp;w.write("abc"); &nbsp;&nbsp;//&nbsp;w&nbsp;goes&nbsp;out&nbsp;of&nbsp;scope&nbsp;here:&nbsp;w.close()&nbsp;is&nbsp;called&nbsp;automatically&nbsp;in&nbsp;ANY&nbsp;case &nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;e)&nbsp;{ &nbsp;&nbsp;throw&nbsp;new&nbsp;RuntimeException(e.getMessage(),&nbsp;e); &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## Thread.interrupted方法誤用 錯誤的寫法: try&nbsp;{ &nbsp;&nbsp;Thread.sleep(1000); &nbsp;&nbsp;}&nbsp;catch&nbsp;(InterruptedException&nbsp;e)&nbsp;{ &nbsp;&nbsp;//&nbsp;ok &nbsp;&nbsp;} &nbsp;&nbsp;or &nbsp;&nbsp;while&nbsp;(true)&nbsp;{ &nbsp;&nbsp;if&nbsp;(Thread.interrupted())&nbsp;break; &nbsp;&nbsp;} 這里主要是interrupted靜態方法除了返回當前線程的中斷狀態, 還會將當前線程狀態復位。 正確的寫法: try&nbsp;{ &nbsp;&nbsp;Thread.sleep(1000); &nbsp;&nbsp;}&nbsp;catch&nbsp;(InterruptedException&nbsp;e)&nbsp;{ &nbsp;&nbsp;Thread.currentThread().interrupt(); &nbsp;&nbsp;} &nbsp;&nbsp;or &nbsp;&nbsp;while&nbsp;(true)&nbsp;{ &nbsp;&nbsp;if&nbsp;(Thread.currentThread().isInterrupted())&nbsp;break; &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 在靜態變量初始化時創建線程 錯誤的寫法: class&nbsp;Cache&nbsp;{ &nbsp;&nbsp;private&nbsp;static&nbsp;final&nbsp;Timer&nbsp;evictor&nbsp;=&nbsp;new&nbsp;Timer(); &nbsp;&nbsp;} Timer構造器內部會new一個thread, 而該thread會從它的父線程(即當前線程)中繼承各種屬性。比如context classloader,?[ThreadLocal](http://www.codeceo.com/article/threadlocal-usage.html "ThreadLocal")以及其他的安全屬性(訪問權限)。 而加載當前類的線程可能是不確定的,比如一個線程池中隨機的一個線程。如果你需要控制線程的屬性,最好的做法就是將其初始化操作放在一個靜態方法中,這樣初始化將由它的調用者來決定。 正確的做法: class&nbsp;Cache&nbsp;{ &nbsp;&nbsp;private&nbsp;static&nbsp;Timer&nbsp;evictor; &nbsp;&nbsp;public&nbsp;static&nbsp;setupEvictor()&nbsp;{ &nbsp;&nbsp;evictor&nbsp;=&nbsp;new&nbsp;Timer(); &nbsp;&nbsp;} &nbsp;&nbsp;} [回到頂部](https://www.cnblogs.com/Qian123/articles/5664924.html#_labelTop) ## 已取消的定時器任務依然持有狀態 錯誤的寫法: final&nbsp;MyClass&nbsp;callback&nbsp;=&nbsp;this; &nbsp;&nbsp;TimerTask&nbsp;task&nbsp;=&nbsp;new&nbsp;TimerTask()&nbsp;{ &nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{ &nbsp;&nbsp;callback.timeout(); &nbsp;&nbsp;} &nbsp;&nbsp;}; &nbsp;&nbsp;timer.schedule(task,&nbsp;300000L); &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;doSomething(); &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;task.cancel(); &nbsp;&nbsp;} 上面的task內部包含一個對外部類實例的應用, 這將導致該引用可能不會被GC立即回收. 因為Timer將保留TimerTask在指定的時間之后才被釋放. 因此task對應的外部類實例將在5分鐘后被回收。 正確的寫法: TimerTask&nbsp;task&nbsp;=&nbsp;new&nbsp;Job(this); &nbsp;&nbsp;timer.schedule(task,&nbsp;300000L); &nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;doSomething(); &nbsp;&nbsp;}&nbsp;finally&nbsp;{ &nbsp;&nbsp;task.cancel(); &nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;static&nbsp;class&nbsp;Job&nbsp;extends&nbsp;TimerTask&nbsp;{ &nbsp;&nbsp;private&nbsp;MyClass&nbsp;callback; &nbsp;&nbsp;public&nbsp;Job(MyClass&nbsp;callback)&nbsp;{ &nbsp;&nbsp;this.callback&nbsp;=&nbsp;callback; &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;boolean&nbsp;cancel()&nbsp;{ &nbsp;&nbsp;callback&nbsp;=&nbsp;null; &nbsp;&nbsp;return&nbsp;super.cancel(); &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;void&nbsp;run()&nbsp;{ &nbsp;&nbsp;if&nbsp;(callback&nbsp;==&nbsp;null)&nbsp;return; &nbsp;&nbsp;callback.timeout(); &nbsp;&nbsp;} &nbsp;&nbsp;}
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看