Java 代碼性能優(yōu)化

作者:Richard_Jason
文章源自:http://blog.csdn.net/richard_jason/article/details/53004974

代碼優(yōu)化侦鹏,一個(gè)很重要的課題喝峦。可能有些人覺得沒用讯榕,一些細(xì)小的地方有什么好修改的,改與不改對(duì)于代碼的運(yùn)行效率有什么影響呢刽虹?這個(gè)問題我是這么考慮的凿滤,就像大海里面的鯨魚一樣,它吃一條小蝦米有用嗎况增?沒用赞庶,但是,吃的小蝦米一多之后,鯨魚就被喂飽了歧强。代碼優(yōu)化也是一樣澜薄,如果項(xiàng)目著眼于盡快無 BUG 上線,那么此時(shí)可以抓大放小摊册,代碼的細(xì)節(jié)可以不精打細(xì)磨肤京;但是如果有足夠的時(shí)間開發(fā)、維護(hù)代碼茅特,這時(shí)候就必須考慮每個(gè)可以優(yōu)化的細(xì)節(jié)了忘分,一個(gè)一個(gè)細(xì)小的優(yōu)化點(diǎn)累積起來,對(duì)于代碼的運(yùn)行效率絕對(duì)是有提升的白修。

代碼優(yōu)化的目標(biāo)是:

  1. 減小代碼的體積
  2. 提高代碼運(yùn)行的效率

代碼優(yōu)化細(xì)節(jié)

1妒峦、盡量指定類、方法的 final 修飾符

帶有 final 修飾符的類是不可派生的兵睛。在 Java 核心 API 中肯骇,有許多應(yīng)用 final 的例子,例如 java.lang.String卤恳,整個(gè)類都是 final 的累盗。為類指定 final 修飾符可以讓類不可以被繼承,為方法指定 final 修飾符可以讓方法不可以被重寫突琳。如果指定了一個(gè)類為 final若债,則該類所有的方法都是 final 的。Java 編譯器會(huì)尋找機(jī)會(huì)內(nèi)聯(lián)所有的 final 方法拆融,內(nèi)聯(lián)對(duì)于提升 Java 運(yùn)行效率作用重大蠢琳,具體參見 Java 運(yùn)行期優(yōu)化。此舉能夠使性能平均提高 50%镜豹。

2傲须、盡量重用對(duì)象

特別是 String 對(duì)象的使用,出現(xiàn)字符串連接時(shí)應(yīng)該使用StringBuilder/StringBuffer代替趟脂。由于 Java 虛擬機(jī)不僅要花時(shí)間生成對(duì)象泰讽,以后可能還需要花時(shí)間對(duì)這些對(duì)象進(jìn)行垃圾回收和處理,因此昔期,生成過多的對(duì)象將會(huì)給程序的性能帶來很大的影響已卸。

3、盡可能使用局部變量

調(diào)用方法時(shí)傳遞的參數(shù)以及在調(diào)用中創(chuàng)建的臨時(shí)變量都保存在棧中速度較快硼一,其他變量累澡,如靜態(tài)變量、實(shí)例變量等般贼,都在堆中創(chuàng)建愧哟,速度較慢奥吩。另外,棧中創(chuàng)建的變量蕊梧,隨著方法的運(yùn)行結(jié)束霞赫,這些內(nèi)容就沒了,不需要額外的垃圾回收望几。

4绩脆、及時(shí)關(guān)閉流

Java 編程過程中,進(jìn)行數(shù)據(jù)庫連接橄抹、I/O 流操作時(shí)務(wù)必小心靴迫,在使用完畢后,及時(shí)關(guān)閉以釋放資源楼誓。因?yàn)閷?duì)這些大對(duì)象的操作會(huì)造成系統(tǒng)大的開銷玉锌,稍有不慎,將會(huì)導(dǎo)致嚴(yán)重的后果疟羹。

5主守、盡量減少對(duì)變量的重復(fù)計(jì)算

明確一個(gè)概念,對(duì)方法的調(diào)用榄融,即使方法中只有一句語句参淫,也是有消耗的,包括創(chuàng)建棧幀愧杯、調(diào)用方法時(shí)保護(hù)現(xiàn)場(chǎng)涎才、調(diào)用方法完畢時(shí)恢復(fù)現(xiàn)場(chǎng)等。所以例如下面的操作:

for (int i = 0; i < list.size(); i++)

{...}

建議替換為:

for (int i = 0, int length = list.size(); i < length; i++)

{...}

這樣力九,在list.size()很大的時(shí)候耍铜,就減少了很多的消耗。

6跌前、盡量采用懶加載的策略棕兼,即在需要的時(shí)候才創(chuàng)建

例如:

String str = "aaa";if (i == 1)
{

list.add(str);

}

建議替換為:

if (i == 1)
{

String str = "aaa";

list.add(str);

}

7、慎用異常

異常對(duì)性能不利抵乓。拋出異常首先要?jiǎng)?chuàng)建一個(gè)新的對(duì)象伴挚,Throwable 接口的構(gòu)造函數(shù)調(diào)用名為fillInStackTrace()的本地同步方法,fillInStackTrace()方法檢查堆棧灾炭,收集調(diào)用跟蹤信息章鲤。只要有異常被拋出,Java 虛擬機(jī)就必須調(diào)整調(diào)用堆棧咆贬,因?yàn)樵谔幚磉^程中創(chuàng)建了一個(gè)新的對(duì)象。異常只能用于錯(cuò)誤處理帚呼,不應(yīng)該用來控制程序流程掏缎。

8皱蹦、不要在循環(huán)中使用 try…catch…,應(yīng)該把其放在最外層

除非不得已眷蜈。如果毫無理由地這么寫了沪哺,只要你的領(lǐng)導(dǎo)資深一點(diǎn)、有強(qiáng)迫癥一點(diǎn)酌儒,八成就要罵你為什么寫出這種垃圾代碼來了辜妓。

9、如果能估計(jì)到待添加的內(nèi)容長度忌怎,為底層以數(shù)組方式實(shí)現(xiàn)的集合籍滴、工具類指定初始長度

比如ArrayListLinkedLlist榴啸、StringBuilder孽惰、StringBufferHashMap鸥印、HashSet等等勋功,以StringBuilder為例:

(1)StringBuilder()      // 默認(rèn)分配16個(gè)字符的空間

(2)StringBuilder(int size)  // 默認(rèn)分配size個(gè)字符的空間

(3)StringBuilder(String str) // 默認(rèn)分配16個(gè)字符+str.length()個(gè)字符空間

可以通過類(這里指的不僅僅是上面的 StringBuilder)的來設(shè)定它的初始化容量,這樣可以明顯地提升性能库说。比如 StringBuilder 吧狂鞋,length 表示當(dāng)前的 StringBuilder 能保持的字符數(shù)量。因?yàn)楫?dāng) StringBuilder 達(dá)到最大容量的時(shí)候潜的,它會(huì)將自身容量增加到當(dāng)前的 2 倍再加 2骚揍,無論何時(shí)只要 StringBuilder 達(dá)到它的最大容量,它就不得不創(chuàng)建一個(gè)新的字符數(shù)組夏块,然后將舊的字符數(shù)組內(nèi)容拷貝到新字符數(shù)組中——這是十分耗費(fèi)性能的一個(gè)操作疏咐。

試想,如果能預(yù)估到字符數(shù)組中大概要存放 5000 個(gè)字符而不指定長度脐供,最接近 5000 的 2 次冪是 4096浑塞,每次擴(kuò)容加的 2 不管,那么:

  1. 在 4096 的基礎(chǔ)上政己,再申請(qǐng) 8194 個(gè)大小的字符數(shù)組酌壕,加起來相當(dāng)于一次申請(qǐng)了 12290 個(gè)大小的字符數(shù)組,如果一開始能指定 5000 個(gè)大小的字符數(shù)組歇由,就節(jié)省了一倍以上的空間卵牍。
  2. 把原來的 4096 個(gè)字符拷貝到新的的字符數(shù)組中去。

這樣沦泌,既浪費(fèi)內(nèi)存空間又降低代碼運(yùn)行效率糊昙。所以,給底層以數(shù)組實(shí)現(xiàn)的集合谢谦、工具類設(shè)置一個(gè)合理的初始化容量是錯(cuò)不了的释牺,這會(huì)帶來立竿見影的效果萝衩。但是,注意没咙,像 HashMap 這種是以數(shù)組+鏈表實(shí)現(xiàn)的集合猩谊,別把初始大小和你估計(jì)的大小設(shè)置得一樣,因?yàn)橐粋€(gè) table 上只連接一個(gè)對(duì)象的可能性幾乎為 0祭刚。初始大小建議設(shè)置為 2 的 N 次冪牌捷,如果能估計(jì)到有 2000 個(gè)元素,設(shè)置成 new HashMap(128)涡驮、new HashMap(256)都可以暗甥。

10、當(dāng)復(fù)制大量數(shù)據(jù)時(shí)遮怜,使用System.arraycopy()命令

11淋袖、乘法和除法使用移位操作

例如:

for (val = 0; val < 100000; val += 5)

{

a = val * 8;
b = val / 2;

}

用移位操作可以極大地提高性能,因?yàn)樵谟?jì)算機(jī)底層锯梁,對(duì)位的操作是最方便即碗、最快的,因此建議修改為:

for (val = 0; val < 100000; val += 5)

{

a = val << 3;
b = val >> 1;

}

移位操作雖然快陌凳,但是可能會(huì)使代碼不太好理解剥懒,因此最好加上相應(yīng)的注釋。

12合敦、循環(huán)內(nèi)不要不斷創(chuàng)建對(duì)象引用

例如:

for (int i = 1; i <= count; i++)

{

Object obj = new Object();

}

這種做法會(huì)導(dǎo)致內(nèi)存中有 count 份 Object 對(duì)象引用存在初橘,count 很大的話,就耗費(fèi)內(nèi)存了充岛,建議為改為:

Object obj = null;for (int i = 0; i <= count; i++) { obj = new Object(); }

這樣的話保檐,內(nèi)存中只有一份 Object 對(duì)象引用,每次new Object()的時(shí)候崔梗, Object 對(duì)象引用指向不同的 Object 罷了夜只,但是內(nèi)存中只有一份,這樣就大大節(jié)省了內(nèi)存空間了蒜魄。

13扔亥、基于效率和類型檢查的考慮,應(yīng)該盡可能使用 array谈为,無法確定數(shù)組大小時(shí)才使用 ArrayList .

14徽诲、盡量使用HashMap酥泞、ArrayList咐鹤、StringBuilder霸旗,除非線程安全需要,否則不推薦使用Hashtable秕脓、Vector柒瓣、StringBuffer瘪菌,后三者由于使用同步機(jī)制而導(dǎo)致了性能開銷

15、不要將數(shù)組聲明為public static final

因?yàn)檫@毫無意義嘹朗,這樣只是定義了引用為static final,數(shù)組的內(nèi)容還是可以隨意改變的诵肛,將數(shù)組聲明為 public 更是一個(gè)安全漏洞屹培,這意味著這個(gè)數(shù)組可以被外部類所改變。

16怔檩、盡量在合適的場(chǎng)合使用單例

使用單例可以減輕加載的負(fù)擔(dān)褪秀、縮短加載的時(shí)間、提高加載的效率薛训,但并不是所有地方都適用于單例媒吗,簡單來說,單例主要適用于以下三個(gè)方面:

  1. 控制資源的使用乙埃,通過線程同步來控制資源的并發(fā)訪問闸英;
  2. 控制實(shí)例的產(chǎn)生,以達(dá)到節(jié)約資源的目的介袜;
  3. 控制數(shù)據(jù)的共享甫何,在不建立直接關(guān)聯(lián)的條件下,讓多個(gè)不相關(guān)的進(jìn)程或線程之間實(shí)現(xiàn)通信遇伞。

17辙喂、盡量避免隨意使用靜態(tài)變量

要知道,當(dāng)某個(gè)對(duì)象被定義為 static 的變量所引用鸠珠,那么 gc 通常是不會(huì)回收這個(gè)對(duì)象所占有的堆內(nèi)存的巍耗,如:

public class A
{ 

private static B b = new B();

}

此時(shí),靜態(tài)變量b的生命周期與A類相同渐排,如果A類不被卸載炬太,那么引用B指向的B對(duì)象會(huì)常駐內(nèi)存,直到程序終止飞盆。

18娄琉、及時(shí)清除不再需要的會(huì)話

為了清除不再活動(dòng)的會(huì)話,許多應(yīng)用服務(wù)器都有默認(rèn)的會(huì)話超時(shí)時(shí)間吓歇,一般為 30 分鐘孽水。當(dāng)應(yīng)用服務(wù)器需要保存更多的會(huì)話時(shí),如果內(nèi)存不足城看,那么操作系統(tǒng)會(huì)把部分?jǐn)?shù)據(jù)轉(zhuǎn)移到磁盤女气,應(yīng)用服務(wù)器也可能根據(jù) MRU(最近最頻繁使用)算法把部分不活躍的會(huì)話轉(zhuǎn)儲(chǔ)到磁盤,甚至可能拋出內(nèi)存不足的異常测柠。如果會(huì)話要被轉(zhuǎn)儲(chǔ)到磁盤炼鞠,那么必須要先被序列化缘滥,在大規(guī)模集群中,對(duì)對(duì)象進(jìn)行序列化的代價(jià)是很昂貴的谒主。因此朝扼,當(dāng)會(huì)話不再需要時(shí),應(yīng)當(dāng)及時(shí)調(diào)用 HttpSession 的 invalidate()方法清除會(huì)話霎肯。

19擎颖、實(shí)現(xiàn) RandomAccess 接口的集合比如 ArrayList,應(yīng)當(dāng)使用最普通的 for 循環(huán)而不是 foreach 循環(huán)來遍歷

這是 JDK 推薦給用戶的观游。JDK API 對(duì)于 RandomAccess 接口的解釋是:實(shí)現(xiàn) RandomAccess 接口用來表明其支持快速隨機(jī)訪問搂捧,此接口的主要目的是允許一般的算法更改其行為,從而將其應(yīng)用到隨機(jī)或連續(xù)訪問列表時(shí)懂缕,能提供良好的性能允跑。

實(shí)際經(jīng)驗(yàn)表明,實(shí)現(xiàn) RandomAccess 接口的類實(shí)例搪柑,假如是隨機(jī)訪問的聋丝,使用普通 for 循環(huán)效率將高于使用 foreach 循環(huán);反過來拌屏,如果是順序訪問的潮针,則使用 Iterator 會(huì)效率更高∫形梗可以使用類似如下的代碼作判斷:

if (list instanceof RandomAccess)

{ for (int i = 0; i < list.size(); i++){}

}else{

Iterator<?> iterator = list.iterable(); while (iterator.hasNext()){iterator.next()}

}

foreach 循環(huán)的底層實(shí)現(xiàn)原理就是迭代器 Iterator每篷,參見 Java 語法糖 1:可變長度參數(shù)以及 foreach 循環(huán)原理。所以后半句”反過來端圈,如果是順序訪問的焦读,則使用 Iterator 會(huì)效率更高”的意思就是順序訪問的那些類實(shí)例,使用 foreach 循環(huán)去遍歷舱权。

20矗晃、使用同步代碼塊替代同步方法

這點(diǎn)在多線程模塊中的 synchronized 鎖方法塊一文中已經(jīng)講得很清楚了,除非能確定一整個(gè)方法都是需要進(jìn)行同步的宴倍,否則盡量使用同步代碼塊张症,避免對(duì)那些不需要進(jìn)行同步的代碼也進(jìn)行了同步,影響了代碼執(zhí)行效率鸵贬。

21俗他、將常量聲明為static final,并以大寫命名

這樣在編譯期間就可以把這些內(nèi)容放入常量池中阔逼,避免運(yùn)行期間計(jì)算生成常量的值兆衅。另外,將常量的名字以大寫命名也可以方便區(qū)分出常量與變量。

22羡亩、不要?jiǎng)?chuàng)建一些不使用的對(duì)象摩疑,不要導(dǎo)入一些不使用的類

這毫無意義,如果代碼中出現(xiàn)”The value of the local variable i is not used”畏铆、”The import java.util is never used”雷袋,那么請(qǐng)刪除這些無用的內(nèi)容。

23辞居、程序運(yùn)行過程中避免使用反射

關(guān)于片排,請(qǐng)參見反射。反射是 Java 提供給用戶一個(gè)很強(qiáng)大的功能速侈,功能強(qiáng)大往往意味著效率不高。不建議在程序運(yùn)行過程中使用尤其是頻繁使用反射機(jī)制迫卢,特別是 Method 的invoke方法倚搬。如果確實(shí)有必要,一種建議性的做法是將那些需要通過反射加載的類乾蛤,在項(xiàng)目啟動(dòng)的時(shí)候通過反射實(shí)例化出一個(gè)對(duì)象并放入內(nèi)存——用戶只關(guān)心和對(duì)端交互的時(shí)候獲取最快的響應(yīng)速度每界,并不關(guān)心對(duì)端的項(xiàng)目啟動(dòng)花多久時(shí)間。

24家卖、使用數(shù)據(jù)庫連接池和線程池

這兩個(gè)池都是用于重用對(duì)象的眨层,前者可以避免頻繁地打開和關(guān)閉連接,后者可以避免頻繁地創(chuàng)建和銷毀線程上荡。

25趴樱、使用帶緩沖的輸入輸出流進(jìn)行 IO 操作

帶緩沖的輸入輸出流:

BufferedReaderBufferedWriter酪捡、BufferedInputStream叁征、BufferedOutputStream,這可以極大地提升 IO 效率逛薇。

26捺疼、順序插入和隨機(jī)訪問比較多的場(chǎng)景使用 ArrayList,元素刪除和中間插入比較多的場(chǎng)景使用 LinkedList

這個(gè)永罚,理解 ArrayList 和 LinkedList 的原理就知道了啤呼。

27、不要讓public方法中有太多的形參

public方法即對(duì)外提供的方法呢袱,如果給這些方法太多形參的話官扣,主要有兩點(diǎn)壞處:

  1. 違反了面向?qū)ο蟮木幊趟枷耄琂ava 講求一切都是對(duì)象产捞,太多的形參醇锚,和面向?qū)ο蟮木幊趟枷氩⒉黄鹾希?/li>
  2. 參數(shù)太多勢(shì)必導(dǎo)致方法調(diào)用的出錯(cuò)概率增加。

至于這個(gè)”太多”指的是多少個(gè),3焊唬、4 個(gè)吧恋昼。比如我們用 JDBC 寫一個(gè)insertStudentInfo方法,有 10 個(gè)學(xué)生信息字段要插如 Student 表中赶促,可以把這 10 個(gè)參數(shù)封裝在一個(gè)實(shí)體類中液肌,作為insert方法的形參。

28鸥滨、字符串變量和字符串常量equals的時(shí)候?qū)⒆址A繉懺谇懊?/strong>

這是一個(gè)比較常見的小技巧了嗦哆,如果有以下代碼:

String str = "123";
if (str.equals("123")) {

...

}

建議修改為:

String str = "123";
if ("123".equals(str))

{

...

}

這么做主要是可以避免空指針異常。

29婿滓、請(qǐng)知道老速,在java中if (i == 1)if (1 == i)是沒有區(qū)別的,但從閱讀習(xí)慣上講凸主,建議使用前者

平時(shí)有人問橘券,if (i == 1)if (1== i)有沒有區(qū)別,這就要從 C/C++ 講起卿吐。

在C/C++中旁舰,if (i == 1)判斷條件成立,是以 0 與非 0 為基準(zhǔn)的嗡官,0 表示 false箭窜,非 0 表示 true,如果有這么一段代碼:

int i = 2;
if (i == 1)

{

...

}else{

...

}

C/C++判斷i==1不成立衍腥,所以 0 表示磺樱,即 false。但是如果:

int i = 2;if (i = 1) { ... }else{ ... }

萬一程序員一個(gè)不小心婆咸,把if (i == 1)寫成if (i = 1)坊罢,這樣就有問題了。在 if 之內(nèi)將i賦值為 1擅耽,if 判斷里面的內(nèi)容非 0活孩,返回的就是 true 了,但是明明i為2乖仇,比較的值是 1憾儒,應(yīng)該返回的 false。這種情況在 C/C++ 的開發(fā)中是很可能發(fā)生的乃沙,并且會(huì)導(dǎo)致一些難以理解的錯(cuò)誤產(chǎn)生起趾。所以,為了避免開發(fā)者在if語句中不正確的賦值操作警儒,建議將 if 語句寫為:

int i = 2;if (1 == i) { ... }else{ ... }

這樣训裆,即使開發(fā)者不小心寫成了1 = i眶根,C/C++編譯器也可以第一時(shí)間檢查出來,因?yàn)槲覀兛梢詫?duì)一個(gè)變量賦值 i 為 1边琉,但是不能對(duì)一個(gè)常量賦值 1 為 i 属百。

但是,在 Java 中变姨,C/C++這種if (i = 1)的語法是不可能出現(xiàn)的族扰,因?yàn)橐坏懥诉@種語法,Java 就會(huì)編譯報(bào)錯(cuò)”Type mismatch: cannot convert from int to boolean”定欧。但是渔呵,盡管 Java 的if (i == 1)if (1 == i)在語義上沒有任何區(qū)別,但是從閱讀習(xí)慣上講砍鸠,建議使用前者會(huì)更好些扩氢。

30、不要對(duì)數(shù)組使用toString()方法

看一下對(duì)數(shù)組使用toString()打印出來的是什么:

public static void main(String[] args)

{ int[] is = new int[]{1, 2, 3};

System.out.println(is.toString());

}

結(jié)果是:

[I@18a992f

本意是想打印出數(shù)組內(nèi)容爷辱,卻有可能因?yàn)閿?shù)組引用 is 為空而導(dǎo)致空指針異常类茂。不過雖然對(duì)數(shù)組toString()沒有意義,但是對(duì)集合toString()是可以打印出集合里面的內(nèi)容的托嚣,因?yàn)榧系母割?code>AbstractCollections重寫了 Object 的toString()方法。

31厚骗、不要對(duì)超出范圍的基本數(shù)據(jù)類型做向下強(qiáng)制轉(zhuǎn)型

這絕不會(huì)得到想要的結(jié)果:

public static void main(String[] args)
{ 

long l = 12345678901234L;

int i = (int)l;

System.out.println(i);

}

我們可能期望得到其中的某幾位示启,但是結(jié)果卻是:

1942892530

解釋一下。Java 中 long 是 8 個(gè)字節(jié) 64 位的领舰,所以 12345678901234 在計(jì)算機(jī)中的表示應(yīng)該是:

0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010

一個(gè)int型數(shù)據(jù)是 4 個(gè)字節(jié) 32 位的夫嗓,從低位取出上面這串二進(jìn)制數(shù)據(jù)的前 32 位是:

0111 0011 1100 1110 0010 1111 1111 0010

這串二進(jìn)制表示為十進(jìn)制 1942892530,所以就是我們上面的控制臺(tái)上輸出的內(nèi)容冲秽。從這個(gè)例子上還能順便得到兩個(gè)結(jié)論:

  1. 整型默認(rèn)的數(shù)據(jù)類型是 int舍咖,long l = 12345678901234L,這個(gè)數(shù)字已經(jīng)超出了int的范圍了锉桑,所以最后有一個(gè) L排霉,表示這是一個(gè) long 型數(shù)。順便民轴,浮點(diǎn)型的默認(rèn)類型是 double攻柠,所以定義 float 的時(shí)候要寫成float f = 3.5f
  2. 接下來再寫一句int ii = l + i;會(huì)報(bào)錯(cuò),因?yàn)?code>long + int是一個(gè) long后裸,不能賦值給 int

32瑰钮、公用的集合類中不使用的數(shù)據(jù)一定要及時(shí) remove 掉

如果一個(gè)集合類是公用的(也就是說不是方法里面的屬性),那么這個(gè)集合里面的元素是不會(huì)自動(dòng)釋放的微驶,因?yàn)槭冀K有引用指向它們浪谴。所以,如果公用集合里面的某些數(shù)據(jù)不使用而不去 remove 掉它們,那么將會(huì)造成這個(gè)公用集合不斷增大苟耻,使得系統(tǒng)有內(nèi)存泄露的隱患篇恒。

33、把一個(gè)基本數(shù)據(jù)類型轉(zhuǎn)為字符串梁呈,基本數(shù)據(jù)類型.toString()是最快的方式婚度、String.valueOf(數(shù)據(jù))次之、數(shù)據(jù)+最慢

把一個(gè)基本數(shù)據(jù)類型轉(zhuǎn)為一般有三種方式官卡,我有一個(gè) Integer 型數(shù)據(jù) i 蝗茁,可以使用 i.toString()String.valueOf(i)寻咒、i+””三種方式哮翘,三種方式的效率如何,看一個(gè)測(cè)試:

public static void main(String[] args)

{ 

int loopTime = 50000;

Integer i = 0; long startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++)

{

String str = String.valueOf(i);

}

System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms");

startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++)

{

String str = i.toString();

}

System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms");

startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++)

{

String str = i + "";

}

System.out.println("i + \"\":" + (System.currentTimeMillis() - startTime) + "ms");

}

運(yùn)行結(jié)果為:

String.valueOf():11ms Integer.toString():5ms i + "":25ms

所以以后遇到把一個(gè)基本數(shù)據(jù)類型轉(zhuǎn)為 String 的時(shí)候毛秘,優(yōu)先考慮使用toString()方法饭寺。至于為什么,很簡單:

  1. String.valueOf()方法底層調(diào)用了Integer.toString()方法叫挟,但是會(huì)在調(diào)用前做空判斷艰匙;

  2. Integer.toString()方法就不說了,直接調(diào)用了抹恳;

  3. i + “”底層使用了StringBuilder實(shí)現(xiàn)员凝,先用append方法拼接,再用toString()方法獲取字符串奋献。

三者對(duì)比下來健霹,明顯是 2 最快、1 次之瓶蚂、3 最慢糖埋。

34、使用最有效率的方式去遍歷 Map

遍歷 Map 的方式有很多窃这,通常場(chǎng)景下我們需要的是遍歷 Map 中的 Key 和 Value 瞳别,那么推薦使用的、效率最高的方式是:

public static void main(String[] args)

{

HashMap<String, String> hm = new HashMap<String, String>();

hm.put("111", "222");

Set<Map.Entry<String, String>> entrySet = hm.entrySet();

Iterator<Map.Entry<String, String>> iter = entrySet.iterator(); while (iter.hasNext())

{

Map.Entry<String, String> entry = iter.next();

System.out.println(entry.getKey() + "\t" + entry.getValue());

}

}

如果你只是想遍歷一下這個(gè) Map 的 key 值杭攻,那用Set keySet = hm.keySet();會(huì)比較合適一些洒试。

35、對(duì)資源的close()建議分開操作

意思是朴上,比如我有這么一段代碼:

try{

XXX.close();

YYY.close();

}catch (Exception e)

{

...

}

建議修改為:

try{ XXX.close(); }catch (Exception e) { ... }try{ YYY.close(); }catch (Exception e) { ... }

雖然有些麻煩垒棋,卻能避免資源泄露。我想痪宰,如果沒有修改過的代碼叼架,萬一XXX.close()拋異常了畔裕,那么就進(jìn)入了 cath 塊中了,YYY.close()不會(huì)執(zhí)行乖订,YYY這塊資源就不會(huì)回收了扮饶,一直占用著,這樣的代碼一多乍构,是可能引起資源句柄泄露的甜无。而改為上面的寫法之后,就保證了無論如何XXXYYY都會(huì)被 close 掉哥遮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末岂丘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子眠饮,更是在濱河造成了極大的恐慌奥帘,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仪召,死亡現(xiàn)場(chǎng)離奇詭異寨蹋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扔茅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門已旧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人召娜,你說我怎么就攤上這事运褪。” “怎么了萤晴?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胁后。 經(jīng)常有香客問我店读,道長,這世上最難降的妖魔是什么攀芯? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任屯断,我火速辦了婚禮,結(jié)果婚禮上侣诺,老公的妹妹穿的比我還像新娘殖演。我一直安慰自己,他們只是感情好年鸳,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布趴久。 她就那樣靜靜地躺著,像睡著了一般搔确。 火紅的嫁衣襯著肌膚如雪彼棍。 梳的紋絲不亂的頭發(fā)上灭忠,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音座硕,去河邊找鬼弛作。 笑死,一個(gè)胖子當(dāng)著我的面吹牛华匾,可吹牛的內(nèi)容都是我干的映琳。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼蜘拉,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼萨西!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起诸尽,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤原杂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后您机,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體穿肄,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年际看,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咸产。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仲闽,死狀恐怖脑溢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赖欣,我是刑警寧澤屑彻,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站顶吮,受9級(jí)特大地震影響社牲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悴了,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一搏恤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧湃交,春花似錦熟空、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至才沧,卻和暖如春阱当,著一層夾襖步出監(jiān)牢的瞬間俏扩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國打工弊添, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留录淡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓油坝,卻偏偏與公主長得像嫉戚,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子澈圈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容