本篇文章給大家分享的是有關(guān)Java編程中最容易忽略的10個(gè)常見(jiàn)問(wèn)題分別有哪些,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。
創(chuàng)新互聯(lián)建站2013年開(kāi)創(chuàng)至今,先為薩迦等服務(wù)建站,薩迦等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為薩迦企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。
在Java編碼中,我們?nèi)菀追敢恍╁e(cuò)誤,也容易疏忽一些問(wèn)題,因此筆者對(duì)日常編碼中曾遇到的一些經(jīng)典情形歸納整理成文,以共同探討。
1. 糾結(jié)的同名
現(xiàn)象
很多類(lèi)的命名相同(例如:常見(jiàn)于異常、常量、日志等類(lèi)),導(dǎo)致在import時(shí),有時(shí)候張冠李戴,這種錯(cuò)誤有時(shí)候很隱蔽。因?yàn)橥念?lèi)功能也類(lèi)似,所以IDE不會(huì)提示warn。
解決
寫(xiě)完代碼時(shí),掃視下import部分,看看有沒(méi)有不熟悉的。替換成正確導(dǎo)入后,要注意下注釋是否也作相應(yīng)修改。
啟示
命名盡量避開(kāi)重復(fù)名,特別要避開(kāi)與JDK中的類(lèi)重名,否則容易導(dǎo)入錯(cuò),同時(shí)存在大量重名類(lèi),在查找時(shí),也需要更多的辨別時(shí)間。
2. 想當(dāng)然的API
現(xiàn)象
有時(shí)候調(diào)用API時(shí),會(huì)想當(dāng)然的通過(guò)名字直接自信滿滿地調(diào)用,導(dǎo)致很驚訝的一些錯(cuò)誤:
示例一:flag是true?
boolean flag = Boolean.getBoolean("true");
可能老是false。
示例二:這是去年的今天嗎(今年是2012年,不考慮閏年)?結(jié)果還是2012年:
Calendar calendar = GregorianCalendar.getInstance();
calendar.roll(Calendar.DAY_OF_YEAR, -365);
下面的才是去年:
calendar.add(Calendar.DAY_OF_YEAR, -365);
解決辦法
問(wèn)自己幾個(gè)問(wèn)題,這個(gè)方法我很熟悉嗎?有沒(méi)有類(lèi)似的API? 區(qū)別是什么?就示例一而言,需要區(qū)別的如下:
Boolean.valueOf(b) VS Boolean.parseBoolean(b) VS Boolean.getBoolean(b);
啟示
名字起的更詳細(xì)點(diǎn),注釋更清楚點(diǎn),不要不經(jīng)了解、測(cè)試就想當(dāng)然的用一些API,如果時(shí)間有限,用自己最為熟悉的API。
3. 有時(shí)候溢出并不難
現(xiàn)象
有時(shí)候溢出并不難,雖然不常復(fù)現(xiàn):
示例一:
long x=Integer.MAX_VALUE+1;
System.out.println(x);
x是多少?竟然是-2147483648,明明加上1之后還是long的范圍。類(lèi)似的經(jīng)常出現(xiàn)在時(shí)間計(jì)算:
數(shù)字1×數(shù)字2×數(shù)字3…
示例二:
在檢查是否為正數(shù)的參數(shù)校驗(yàn)中,為了避免重載,選用參數(shù)number, 于是下面代碼結(jié)果小于0,也是因?yàn)橐绯鰧?dǎo)致:
Number i=Long.MAX_VALUE;
System.out.println(i.intValue()>0);
解決
讓***個(gè)操作數(shù)是long型,例如加上L或者l(不建議小寫(xiě)字母l,因?yàn)楹蛿?shù)字1太相似了);
不確定時(shí),還是使用重載吧,即使用doubleValue(),當(dāng)參數(shù)是BigDecimal參數(shù)時(shí),也不能解決問(wèn)題。
啟示
對(duì)數(shù)字運(yùn)用要保持敏感:涉及數(shù)字計(jì)算就要考慮溢出;涉及除法就要考慮被除數(shù)是0;實(shí)在容納不下了可以考慮BigDecimal之類(lèi)。
4. 日志跑哪了?
現(xiàn)象
有時(shí)候覺(jué)得log都打了,怎么找不到?
示例一:沒(méi)有stack trace!
} catch (Exception ex) {
log.error(ex);
}
示例二:找不到log!
} catch (ConfigurationException e) {
e.printStackTrace();
}
解決
替換成log.error(ex.getMessage(),ex);
換成普通的log4j吧,而不是System.out。
啟示
API定義應(yīng)該避免讓人犯錯(cuò),如果多加個(gè)重載的log.error(Exception)自然沒(méi)有錯(cuò)誤發(fā)生
在產(chǎn)品代碼中,使用的一些方法要考慮是否有效,使用e.printStackTrace()要想下終端(Console)在哪。
5. 遺忘的Volatile
現(xiàn)象
在DCL模式中,總是忘記加一個(gè)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;
}
解決
毋庸置疑,加上一個(gè)吧,synchronized 鎖的是一塊代碼(整個(gè)方法或某個(gè)代碼塊),保證的是這”塊“代碼的可見(jiàn)性及原子性,但是instance == null***次判斷時(shí)不再范圍內(nèi)的。所以可能讀出的是過(guò)期的null。
啟示
我們總是覺(jué)得某些低概率的事件很難發(fā)生,例如某個(gè)時(shí)間并發(fā)的可能性、某個(gè)異常拋出的可能性,所以不加控制,但是如果可以,還是按照前人的“***實(shí)踐”來(lái)寫(xiě)代碼吧。至少不用過(guò)多解釋為啥另辟蹊徑。
6. 不要影響彼此
現(xiàn)象
在釋放多個(gè)IO資源時(shí),都會(huì)拋出IOException ,于是可能為了省事如此寫(xiě):
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();
}
}
假設(shè)bos關(guān)閉失敗,bis還能關(guān)閉嗎?當(dāng)然不能!
解決辦法
雖然拋出的是同一個(gè)異常,但是還是各自捕獲各的為好。否則***個(gè)失敗,后一個(gè)面就沒(méi)有機(jī)會(huì)去釋放資源了。
啟示
代碼/模塊之間可能存在依賴,要充分識(shí)別對(duì)相互的依賴。
7. 用斷言取代參數(shù)校驗(yàn)
現(xiàn)象
如題所提,作為防御式編程常用的方式:斷言,寫(xiě)在產(chǎn)品代碼中做參數(shù)校驗(yàn)等。例如:
private void send(List< Event> eventList) {
assert eventList != null;
}
解決
換成正常的統(tǒng)一的參數(shù)校驗(yàn)方法。因?yàn)閿嘌阅J(rèn)是關(guān)閉的,所以起不起作用完全在于配置,如果采用默認(rèn)配置,經(jīng)歷了eventList != null結(jié)果還沒(méi)有起到作用,徒勞無(wú)功。
啟示
有的時(shí)候,代碼起不起作用,不僅在于用例,還在于配置,例如斷言是否啟用、log級(jí)別等,要結(jié)合真實(shí)環(huán)境做有用編碼。
8. 用戶認(rèn)知負(fù)擔(dān)有時(shí)候很重
現(xiàn)象
先來(lái)比較三組例子,看看那些看著更順暢?
示例一:
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)
解決
保持參數(shù)傳遞順序;
remove變成了delete,顯得突兀了點(diǎn), 統(tǒng)一表達(dá)更好;
保持表達(dá),少縮寫(xiě)也會(huì)看起來(lái)流暢點(diǎn)。
啟示
在編碼過(guò)程中,不管是參數(shù)的順序還是命名都盡量統(tǒng)一,這樣用戶的認(rèn)知負(fù)擔(dān)會(huì)很少,不要要用戶容易犯錯(cuò)或迷惑。例如用枚舉代替string從而不讓用戶迷惑到底傳什么string, 諸如此類(lèi)。
9. 忽視日志記錄時(shí)機(jī)、級(jí)別
現(xiàn)象
存在下面兩則示例:
示例一:該不該記錄日志?
catch (SocketException e)
{
LOG.error("server error", e);
throw new ConnectionException(e.getMessage(), e);
}
示例二:記什么級(jí)別日志?
在用戶登錄系統(tǒng)中,每次失敗登錄:
LOG.warn("Failed to login by "+username+");
解決
移除日志記錄:在遇到需要re-throw的異常時(shí),如果每個(gè)人都按照先記錄后throw的方式去處理,那么對(duì)一個(gè)錯(cuò)誤會(huì)記錄太多的日志,所以不 推薦如此做;但是如果re-throw出去的exception沒(méi)有帶完整的trace( 即cause),那么***還是記錄下。
如果惡意登錄,那系統(tǒng)內(nèi)部會(huì)出現(xiàn)太多WARN,從而讓管理員誤以為是代碼錯(cuò)誤??梢苑答佊脩粢藻e(cuò)誤,但是不要記錄用戶錯(cuò)誤的行為,除非想達(dá)到控制的目的。
啟示
日志改不改記?記成什么級(jí)別?如何記?這些都是問(wèn)題,一定要根據(jù)具體情況,需要考慮:
是用戶行為錯(cuò)誤還是代碼錯(cuò)誤?
記錄下來(lái)的日志,能否能給別人在不造成過(guò)多的干擾前提下提供有用的信息以快速定位問(wèn)題。
10. 忘設(shè)初始容量
現(xiàn)象
在JAVA中,我們常用Collection中的Map做Cache,但是我們經(jīng)常會(huì)遺忘設(shè)置初始容量。
cache = new LRULinkedHashMap< K, V>(maxCapacity);
解決
初始容量的影響有多大?拿LinkedHashMap來(lái)說(shuō),初始容量如果不設(shè)置默認(rèn)是16,超過(guò)16×LOAD_FACTOR,會(huì)resize(2 * table.length),擴(kuò)大2倍:采用 Entry[] newTable = new Entry[newCapacity]; transfer(newTable),即整個(gè)數(shù)組Copy, 那么對(duì)于一個(gè)需要做大容量CACHE來(lái)說(shuō),從16變成一個(gè)很大的數(shù)量,需要做多少次數(shù)組復(fù)制可想而知。如果初始容量就設(shè)置很大,自然會(huì)減少resize, 不過(guò)可能會(huì)擔(dān)心,初始容量設(shè)置很大時(shí),沒(méi)有Cache內(nèi)容仍然會(huì)占用過(guò)大體積。其實(shí)可以參考以下表格簡(jiǎn)單計(jì)算下, 初始時(shí)還沒(méi)有cache內(nèi)容, 每個(gè)對(duì)象僅僅是4字節(jié)引用而已。
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的過(guò)程,如果數(shù)據(jù)量很大,就不能忽視初始容量可以考慮設(shè)置下,否則不僅有頻繁的 resize還容易浪費(fèi)容量。
在Java編程中,除了上面枚舉的一些容易忽視的問(wèn)題,日常實(shí)踐中還存在很多。
以上就是Java編程中最容易忽略的10個(gè)常見(jiàn)問(wèn)題分別有哪些,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前名稱(chēng):Java編程中最容易忽略的10個(gè)常見(jiàn)問(wèn)題分別有哪些
網(wǎng)站網(wǎng)址:http://jinyejixie.com/article32/gggcsc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、微信小程序、用戶體驗(yàn)、云服務(wù)器、做網(wǎng)站、網(wǎng)頁(yè)設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)