自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java暗箱操作之自動(dòng)裝箱與拆箱

開(kāi)發(fā) 后端
  ArrayList用起來(lái)是多么的順手!當(dāng)時(shí)我只知道尖括號(hào)<>里面只能加入大寫字母開(kāi)頭的Object類型,不能加入int、char、double這些原始類型,至于原因沒(méi)研究過(guò),這么規(guī)定就這么用唄。

我以前在寫Android項(xiàng)目的時(shí)候,估計(jì)寫得最多最熟練的幾句話就是:

  1. List<Integer> list = new ArrayList<Integer>(); list.add(1);   //把一個(gè)整數(shù)加入到集合中 
  2. int i = list.get(0);    //從集合中取出元素 

  ArrayList用起來(lái)是多么的順手!當(dāng)時(shí)我只知道尖括號(hào)<>里面只能加入大寫字母開(kāi)頭的Object類型,不能加入int、char、double這些原始類型,至于原因沒(méi)研究過(guò),這么規(guī)定就這么用唄。

[[147738]]

  但是隨著對(duì)“碼農(nóng)”式無(wú)腦學(xué)習(xí)法的逐漸厭倦,我開(kāi)始重新審視Java代碼內(nèi)部的東西。

  首當(dāng)其沖的就是每個(gè)項(xiàng)目一定用到的ArrayList。在我的另一篇博客中已經(jīng)對(duì)ArrayList的源碼實(shí)現(xiàn)做了大體的分析。然而還有幾個(gè)源碼中看不出來(lái),但是確實(shí)存在疑點(diǎn)的問(wèn)題亟待解決。

  1. List<Integer> list = new ArrayList<Integer>(); 

這句代碼中每個(gè)元素是Integer類型,那么往list里面add新元素的時(shí)候必須為Integer,比如加個(gè)String進(jìn)去,代碼下面就會(huì)出現(xiàn)紅色波浪線。
但是這句list.add(1) 眾所周知,代碼里面隨便寫個(gè)不帶小數(shù)點(diǎn)的數(shù)字,那它就是個(gè)int;把一個(gè)int加到一個(gè)只能有Integer的List中不報(bào)錯(cuò),不覺(jué)得有貓膩嗎?

同樣地,int i = list.get(0),取出list中索引為0的元素,也應(yīng)該是個(gè)Integer,為什么接收的變量就是個(gè)int呢?這是一個(gè)多么明顯的類型不匹配錯(cuò)誤啊!

以前,我確實(shí)聽(tīng)說(shuō)過(guò)“包裝類”這個(gè)概念,但是忽視了它,因?yàn)槲乙恢庇X(jué)得Integer,F(xiàn)loat這些東西,說(shuō)難聽(tīng)點(diǎn)就是擺出來(lái)裝裝逼的,只是因?yàn)長(zhǎng)ist不接受int,float類型,迫不得已發(fā)明了Integer,Float,實(shí)際并沒(méi)有卵用。

最近看了《Effective Java》里面的一節(jié),名字叫“Prefer primitive types to boxed primitives”。里面羅列了很多原始類型和包裝類型混用的例子,搞得我暈頭轉(zhuǎn)向的。下面是其中一段代碼:

  1. Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) {      sum += i;   } System.out.println(sum); 

據(jù)書中講,這是一段運(yùn)行效率低到不可救藥的代碼,你能看出其中的問(wèn)題嗎?
反正我當(dāng)時(shí)看到這段代碼就明顯感覺(jué)到,Java對(duì)于原始類型與相應(yīng)的Object類型的轉(zhuǎn)化,在編譯過(guò)程中肯定做了什么見(jiàn)不得人的事情……

下面正式引出本文的話題:AutoBoxing and Unboxing(自動(dòng)裝箱&自動(dòng)拆箱)

看一個(gè)最簡(jiǎn)單的例子:

  1. Character ch = 'a';  //Character是char的包裝類 

這里沒(méi)有出現(xiàn)任何錯(cuò)誤,其實(shí)編譯器在代碼優(yōu)化的時(shí)候,暗中轉(zhuǎn)化成了下面的代碼:

  1. Character ch = Character.valueOf('a');  

這就是說(shuō),"="右側(cè)自動(dòng)調(diào)用Character類對(duì)應(yīng)的靜態(tài)方法構(gòu)造出了一個(gè)Character的實(shí)例。
為了進(jìn)一步說(shuō)明,這里稍微看一下valueOf方法

 

  1. public static Character valueOf(char c) {         return c < 128 ? SMALL_VALUES[c] : new Character(c);     }   //如果字符在緩沖區(qū)中,直接取出Character實(shí)例,否則要重新構(gòu)造 
  2.  
  3.    private static final Character[] SMALL_VALUES = new Character[128];  //類中自帶一個(gè)靜態(tài)的緩沖區(qū),保存128個(gè)常用ASCII碼字符對(duì)應(yīng)的Character實(shí)例,免去每次重新構(gòu)造實(shí)例的麻煩 
  4.  
  5.    static {         for (int i = 0; i < 128; i++) {             SMALL_VALUES[i] = new Character((char) i);  //調(diào)用構(gòu)造函數(shù) 
  6.        }     } 

 

對(duì)于Integer等其他包裝類,自身都帶有一個(gè)靜態(tài)的valueOf方法。每次編譯器檢查到需要把一個(gè)int傳給Integer時(shí),就自動(dòng)對(duì)代碼進(jìn)行轉(zhuǎn)化。
比如上面的list.add(1),在編譯過(guò)程中編譯器發(fā)現(xiàn)要傳進(jìn)去的參數(shù)是int,但是要接收的是Integer,于是代碼變?yōu)椋?/span>

  1. list.add(Integer.valueOf(1)); 

以上就是自動(dòng)裝箱(auto-boxing)的過(guò)程。

自動(dòng)裝箱一般在兩種情況下會(huì)發(fā)生(以int和Integer為例):
1、把int作為一個(gè)方法的參數(shù)傳進(jìn)來(lái),但是方法體里面希望得到的參數(shù)是Integer;
2、在賦值過(guò)程中,"="左邊是Integer變量,右邊是int變量。

這樣一來(lái),自動(dòng)拆箱的過(guò)程就順理成章了??匆韵麓a:

 

  1. public static int sumEven(List<Integer> li) {     int sum = 0;     for (Integer i: li)         if (i % 2 == 0)             sum += i;         return sum; } 

 

在循環(huán)體內(nèi)做了兩次拆箱操作,編譯器會(huì)轉(zhuǎn)換成以下代碼:

 

  1. public static int sumEven(List<Integer> li) {     int sum = 0;     for (Integer i: li)         if (i.intValue() % 2 == 0)             sum += i.intValue();         return sum; } 

 

Integer的intValue方法就簡(jiǎn)單多了,直接返回被包裝的int值

  1. @Override     public int intValue() {         return value;    //value是Integer的成員變量  
  2. }
自動(dòng)拆箱的用處跟自動(dòng)裝箱正好相反,也是用在參數(shù)傳遞和賦值過(guò)程中,這里就不贅述了。
我們?cè)賮?lái)分析一下那段超級(jí)低效的代碼吧,經(jīng)過(guò)自動(dòng)拆裝箱轉(zhuǎn)換之后應(yīng)該是這樣子的:

  1. Long sum = Long.valueOf(0L); for (long i = 0; i < Integer.MAX_VALUE; i++) {      sum = Long.valueOf(sum.longValue() + i);   //低效所在 
  2. } System.out.println(sum.toString()); 

在循環(huán)體里面,簡(jiǎn)簡(jiǎn)單單只有一句話,竟然包含一次拆箱和一次裝箱操作,在經(jīng)過(guò)20多億次的循環(huán)之后,效率損耗得難以置信!
既然拆箱和裝箱可以看做“逆運(yùn)算”,那么為什么還要進(jìn)行多余的操作呢?直接用原始值運(yùn)算,然后一次裝箱不是更省事嗎

  1. Long sum = 0L; long s = sum; for (long i = 0; i < Integer.MAX_VALUE; i++) {      s += i;   } sum = Long.valueOf(s); System.out.println(sum);

參考資料:https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

 

 

 

責(zé)任編輯:王雪燕 來(lái)源: 博客園
相關(guān)推薦

2020-11-02 13:06:42

Java裝箱拆箱

2012-03-26 11:32:45

Java

2009-08-26 03:39:00

C#裝箱和拆箱

2021-09-06 14:30:34

C#裝箱拆箱

2009-08-28 11:22:11

C#裝箱和拆箱

2009-08-06 15:40:11

C#裝箱和拆箱

2009-09-01 17:51:47

C#拆箱C#裝箱

2009-10-22 19:11:25

CLR Via C#教

2021-02-28 21:47:51

Java語(yǔ)法糖算數(shù)

2019-10-30 16:03:48

JavaJava虛擬機(jī)數(shù)據(jù)庫(kù)

2018-09-05 15:51:25

Java自動(dòng)拆裝箱

2015-09-28 08:46:19

Java自動(dòng)裝箱性能

2009-07-07 17:56:00

JDK1.5封箱及拆箱

2009-08-11 15:17:12

C#基礎(chǔ)知識(shí)

2010-05-04 08:58:02

.NET

2023-09-07 08:05:32

三元表達(dá)式自動(dòng)

2010-11-11 11:19:19

騰訊

2009-08-11 14:20:41

C# .NET學(xué)習(xí)經(jīng)驗(yàn)

2021-12-20 22:58:23

手機(jī)內(nèi)存技術(shù)

2009-06-08 22:03:37

裝箱問(wèn)題Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)