給你Java學習路線:html-css-js-jq-javase-數(shù)據(jù)庫-jsp-servlet-Struts2-hibernate-mybatis-spring4-springmvc-ssh-ssm
在Java編碼中财岔,我們?nèi)菀追敢恍╁e誤同规,也容易疏忽一些問題盯孙,因此筆者對日常編碼中曾遇到的一些經(jīng)典情形歸納整理成文,以共同探討宫患。
1. 糾結的同名
現(xiàn)象
很多類的命名相同(例如:常見于異常、常量铐伴、日志等類)撮奏,導致在import時,有時候張冠李戴当宴,這種錯誤有時候很隱蔽畜吊。因為往往同名的類功能也類似,所以IDE不會提示warn户矢。
解決
寫完代碼時玲献,掃視下import部分,看看有沒有不熟悉的。替換成正確導入后捌年,要注意下注釋是否也作相應修改瓢娜。
啟示
命名盡量避開重復名,特別要避開與JDK中的類重名礼预,否則容易導入錯眠砾,同時存在大量重名類,在查找時托酸,也需要更多的辨別時間褒颈。
小編推薦一個學Java的學習裙【 六五零,五五四励堡,六零七 】谷丸,無論你是大牛還是小白,是想轉行還是想入行都可以來了解一起進步一起學習应结!裙內(nèi)有開發(fā)工具刨疼,很多干貨和技術資料分享!
2. 想當然的API
現(xiàn)象
有時候調(diào)用API時鹅龄,會想當然的通過名字直接自信滿滿地調(diào)用揩慕,導致很驚訝的一些錯誤:
示例一:flag是true?
booleanflag = 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? 區(qū)別是什么?就示例一而言侨糟,需要區(qū)別的如下:
Boolean.valueOf(b)VSBoolean.parseBoolean(b)VSBoolean.getBoolean(b);
啟示
名字起的更詳細點碍扔,注釋更清楚點,不要不經(jīng)了解秕重、測試就想當然的用一些API不同,如果時間有限,用自己最為熟悉的API溶耘。
3. 有時候溢出并不難
現(xiàn)象
有時候溢出并不難二拐,雖然不常復現(xiàn):
示例一:
longx=Integer.MAX_VALUE+1;System.out.println(x);
x是多少?竟然是-2147483648凳兵,明明加上1之后還是long的范圍百新。類似的經(jīng)常出現(xiàn)在時間計算:
數(shù)字1×數(shù)字2×數(shù)字3…
示例二:
在檢查是否為正數(shù)的參數(shù)校驗中,為了避免重載庐扫,選用參數(shù)number, 于是下面代碼結果小于0饭望,也是因為溢出導致:
Numberi=Long.MAX_VALUE;System.out.println(i.intValue()>0);
解決
讓第一個操作數(shù)是long型仗哨,例如加上L或者l(不建議小寫字母l,因為和數(shù)字1太相似了)铅辞;
不確定時厌漂,還是使用重載吧,即使用doubleValue()斟珊,當參數(shù)是BigDecimal參數(shù)時苇倡,也不能解決問題。
啟示
對數(shù)字運用要保持敏感:涉及數(shù)字計算就要考慮溢出囤踩;涉及除法就要考慮被除數(shù)是0雏节;實在容納不下了可以考慮BigDecimal之類。
4. 日志跑哪了高职?
現(xiàn)象
有時候覺得log都打了,怎么找不到辞州?
示例一:沒有stack trace怔锌!
}catch(Exceptionex) { ? ?log.error(ex); }
示例二:找不到log!
}catch(ConfigurationExceptione) { ? ?e.printStackTrace();}
解決
替換成log.error(ex.getMessage(),ex);
換成普通的log4j吧变过,而不是System.out埃元。
啟示
API定義應該避免讓人犯錯,如果多加個重載的log.error(Exception)自然沒有錯誤發(fā)生
在產(chǎn)品代碼中媚狰,使用的一些方法要考慮是否有效岛杀,使用e.printStackTrace()要想下終端(Console)在哪。
5. 遺忘的Volatile
現(xiàn)象
在DCL模式中崭孤,總是忘記加一個Volatile类嗤。
privatestaticCacheImpl instance;//lose volatilepublicstaticCacheImplgetInstance(){if(instance ==null) {synchronized(CacheImpl.class) {if(instance ==null) { ? ? ? ? ? ? ? ?instance =newCacheImpl ();? ? ? ? ? ? ?} ? ? ? ?} ? ?}returninstance;}
解決
毋庸置疑,加上一個吧辨宠,synchronized 鎖的是一塊代碼(整個方法或某個代碼塊)遗锣,保證的是這”塊“代碼的可見性及原子性,但是instance == null第一次判斷時不再范圍內(nèi)的嗤形。所以可能讀出的是過期的null精偿。
啟示
我們總是覺得某些低概率的事件很難發(fā)生,例如某個時間并發(fā)的可能性赋兵、某個異常拋出的可能性笔咽,所以不加控制,但是如果可以霹期,還是按照前人的“最佳實踐”來寫代碼吧叶组。至少不用過多解釋為啥另辟蹊徑。
小編推薦一個學Java的學習裙【 六五零经伙,五五四扶叉,六零七 】勿锅,無論你是大牛還是小白,是想轉行還是想入行都可以來了解一起進步一起學習枣氧!裙內(nèi)有開發(fā)工具溢十,很多干貨和技術資料分享!
6. 不要影響彼此
現(xiàn)象
在釋放多個IO資源時达吞,都會拋出IOException 张弛,于是可能為了省事如此寫:
publicstaticvoidinputToOutput(InputStream is, OutputStream os,booleanisClose)throwsIOException { ? ?BufferedInputStream bis =newBufferedInputStream(is,1024); ? ?BufferedOutputStream bos =newBufferedOutputStream(os,1024); ? ? ?….if(isClose) { ? ? ? bos.close(); ? ? ? bis.close(); ? ?}}
假設bos關閉失敗,bis還能關閉嗎酪劫?當然不能吞鸭!
解決辦法
雖然拋出的是同一個異常,但是還是各自捕獲各的為好覆糟。否則第一個失敗刻剥,后一個面就沒有機會去釋放資源了。
啟示
代碼/模塊之間可能存在依賴滩字,要充分識別對相互的依賴造虏。
7. 用斷言取代參數(shù)校驗
現(xiàn)象
如題所提,作為防御式編程常用的方式:斷言麦箍,寫在產(chǎn)品代碼中做參數(shù)校驗等漓藕。例如:
privatevoidsend(List< Event> eventList){asserteventList !=null;}
解決
換成正常的統(tǒng)一的參數(shù)校驗方法。因為斷言默認是關閉的挟裂,所以起不起作用完全在于配置享钞,如果采用默認配置,經(jīng)歷了eventList != null結果還沒有起到作用诀蓉,徒勞無功栗竖。
啟示
有的時候,代碼起不起作用交排,不僅在于用例划滋,還在于配置,例如斷言是否啟用埃篓、log級別等处坪,要結合真實環(huán)境做有用編碼。
8. 用戶認知負擔有時候很重
現(xiàn)象
先來比較三組例子架专,看看那些看著更順暢同窘?
示例一:
publicvoidcaller(inta, String b,floatc, String d){ ? ?methodOne(d, z, b); ? ?methodTwo(b, c, d);}publicvoidmethodOne(String d,floatz, String b)publicvoidmethodTwo(String b,floatc, String d)
示例二:
publicbooleanremove(String key,longtimeout){ ? ? ? ? ? ? Future< Boolean> future = memcachedClient.delete(key);publicbooleandelete(String key,longtimeout){ ? ? ? ? ? ? Future< Boolean> future = memcachedClient.delete(key);
示例三:
publicstaticStringgetDigest(StringfilePath, DigestAlgorithm algorithm)publicstaticStringgetDigest(StringfilePath, DigestAlgorithm digestAlgorithm)
解決
保持參數(shù)傳遞順序;
remove變成了delete部脚,顯得突兀了點想邦, 統(tǒng)一表達更好;
保持表達委刘,少縮寫也會看起來流暢點丧没。
啟示
在編碼過程中鹰椒,不管是參數(shù)的順序還是命名都盡量統(tǒng)一,這樣用戶的認知負擔會很少呕童,不要要用戶容易犯錯或迷惑漆际。例如用枚舉代替string從而不讓用戶迷惑到底傳什么string, 諸如此類。
9. 忽視日志記錄時機夺饲、級別
現(xiàn)象
存在下面兩則示例:
示例一:該不該記錄日志奸汇?
catch(SocketException e){ ? ?LOG.error("server error", e);thrownewConnectionException(e.getMessage(), e);}
示例二:記什么級別日志?
在用戶登錄系統(tǒng)中往声,每次失敗登錄:
LOG.warn("Failed to login by "+username+");
解決
移除日志記錄:在遇到需要re-throw的異常時擂找,如果每個人都按照先記錄后throw的方式去處理,那么對一個錯誤會記錄太多的日志浩销,所以不推薦如此做贯涎;但是如果re-throw出去的exception沒有帶完整的trace( 即cause),那么最好還是記錄下慢洋。
如果惡意登錄柬采,那系統(tǒng)內(nèi)部會出現(xiàn)太多WARN,從而讓管理員誤以為是代碼錯誤且警。可以反饋用戶以錯誤礁遣,但是不要記錄用戶錯誤的行為斑芜,除非想達到控制的目的。
啟示
日志改不改記祟霍?記成什么級別杏头?如何記?這些都是問題沸呐,一定要根據(jù)具體情況醇王,需要考慮:
是用戶行為錯誤還是代碼錯誤?
記錄下來的日志崭添,能否能給別人在不造成過多的干擾前提下提供有用的信息以快速定位問題寓娩。
10. 忘設初始容量
現(xiàn)象
在JAVA中,我們常用Collection中的Map做Cache,但是我們經(jīng)常會遺忘設置初始容量呼渣。
cache=newLRULinkedHashMap< K, V>(maxCapacity);
解決
初始容量的影響有多大棘伴?拿LinkedHashMap來說,初始容量如果不設置默認是16屁置,超過16×LOAD_FACTOR,會resize(2 * table.length),擴大2倍:采用 Entry[] newTable = new Entry[newCapacity]; transfer(newTable)焊夸,即整個數(shù)組Copy, 那么對于一個需要做大容量CACHE來說蓝角,從16變成一個很大的數(shù)量阱穗,需要做多少次數(shù)組復制可想而知饭冬。如果初始容量就設置很大,自然會減少resize, 不過可能會擔心揪阶,初始容量設置很大時昌抠,沒有Cache內(nèi)容仍然會占用過大體積。其實可以參考以下表格簡單計算下, 初始時還沒有cache內(nèi)容, 每個對象僅僅是4字節(jié)引用而已遣钳。
memory for reference fields (4 bytes each);
memory for primitive fields
Java typeBytes required
boolean1
byte
char2
short
int4
float
long8
double
啟示:小編推薦一個學Java的學習裙【 六五零扰魂,五五四,六零七 】蕴茴,無論你是大牛還是小白劝评,是想轉行還是想入行都可以來了解一起進步一起學習!裙內(nèi)有開發(fā)工具倦淀,很多干貨和技術資料分享蒋畜!
不僅是map, 還有stringBuffer等,都有容量resize的過程撞叽,如果數(shù)據(jù)量很大姻成,就不能忽視初始容量可以考慮設置下,否則不僅有頻繁的 resize還容易浪費容量愿棋。
在Java編程中科展,除了上面枚舉的一些容易忽視的問題,日常實踐中還存在很多糠雨。相信通過不斷的總結和努力才睹,可以將我們的程序完美呈現(xiàn)給讀者。
Java是一種可以撰寫跨平臺應用軟件的面向對象的程序設計語言甘邀。Java 技術具有卓越的通用性琅攘、高效性、平臺移植性和安全性松邪,廣泛應用于PC坞琴、數(shù)據(jù)中心、游戲控制臺逗抑、科學超級計算機剧辐、移動電話和互聯(lián)網(wǎng),同時擁有全球最大的開發(fā)者專業(yè)社群邮府。