深刻淺出了解「裝箱與拆箱」

關注我
深刻淺出了解「裝箱與拆箱」

每章一點正能量:每當你想要放棄的時候,就想一想是爲了什麼才一路堅持到如今。html

前言

最近在回顧複習Java基礎中的一些知識點,發現了一些之前見過可是沒有留意卻特別有意思的知識特性,好比此次想分享的Java中一個常見的特性:自動裝箱與拆箱。這個知識點和特性其實在咱們開發過程當中常常會遇到。同時咱們也會去使用一些基本數據類型或者是封裝數據類型,可是對於他們之間的一些轉換等特性可能不是特別清楚。也可能出如今咱們的面試中。本章部份內容從源碼中解讀一些自動裝箱與拆箱的原理,以及會出現的一些陷阱已經性能等。若有錯誤還請你們及時指出~java

本文已同步至 GitHub/Gitee/公衆號,感興趣的同窗幫忙點波關注~git

問題:github

  • 基本數據類型與封裝數據類型有哪些區別?
  • 什麼是裝箱?什麼是拆箱?
  • 裝箱和拆箱都是如何實現的?
  • 使用時須要注意哪些問題?

1.基礎知識回顧

  1. Java把內存劃分紅兩種:一種是棧內存,另外一種是堆內存。面試

  2. int是基本類型,直接存數值;而 Integer是類,產生對象時用一個引用指向這個對象。數據庫

  3. 封裝類位於java.lang包中。編程

  4. 封裝類是引用傳遞而基本類型是值傳遞數組

  5. 基本類型的變量和對象的引用變量都是在函數的棧內存中分配 ,而實際的對象是在存儲堆內存中緩存

1.1 基本數據類型和封裝類型的區別

咱們來看下他們之間有哪些區別: bash

區別

八種基本數據類型分別是:byte、char、boolean、int、short、float、double、long; 對應的封裝類型分別是:Byte、Character、Boolean、Integer、Short、Float、Double、Long。

基本類型 封裝類型 字節長度 默認值
boolean Boolean 1 false
byte Byte 1 0
char Character 2 u0000
short Short 2 0
int integer 4 0
long Long 8 0l或0L
float Float 4 0.0f或0.0F
double Double 8 0.0

2. "==" 和 "equal()" 方法

在鞏固了上面的基礎知識點以後,咱們再來看下另外的一個知識點 "=="和"equal()" 這兩個判斷符在比較基本數據類型和封裝類型的時候會作的一些事情。

" == ":比較的是基本數據類型,比較的是它們的值

"equals()": 比較的是引用數據類型,根據不一樣的數據類型調用不一樣的equals方法。在特殊狀況下能夠重寫equals方法。

a==b並不能判斷a等於b,而是判斷是否爲同一個Object。若是咱們要判斷他們的值怎麼作呢?用equal或者Objects.equals()(JDK1.7以後新加 的語法)

Objects.equals有什麼好處呢?

若是用a.equals(b) 若是a是null 的話,還會拋出空指針異常。可是用Objects.equals就沒有問題。所以咱們在使用引用類型的時候須要注意,當咱們在賦值的時候,兩個變量都是引用同一個Object。

咱們以 int與Integer 做爲例子,看下"=="和"equal()"方法:

1)基本型和封裝類型進行"=="運算符的比較,封裝類型將會自動拆箱變爲基本型後再進行比較,所以Integer(0)會自動拆箱爲int類型再進行比較。

2)兩個Integer類型進行"=="比較,若是其值在-128至127,那麼返回true,不然返回false, 這跟Integer.valueOf()的緩衝對象有關,後面會說。

3)兩個封裝類型進行equals()比較,首先equals()會比較類型,若是類型相同,則繼續比較值,若是值也相同,返回true。

4)基本型封裝類型調用equals(),可是參數是基本類型,這時候,先會進行自動裝箱,基本型轉換爲其封裝類型,再進行3中的比較。

3.什麼是裝箱和拆箱

基本數據(Primitive)類型的自動裝箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0開始提供的功能。Java語言規範中說道:在許多狀況下包裝與解包裝是由編譯器自行完成的(在這種狀況下包裝稱爲裝箱,解包裝稱爲拆箱)。 通俗的理解:裝箱:基本類型轉換成封裝類型, 拆箱:封裝類型轉換成基本類型 這麼一個過程。在上面有介紹八種基本類型和對應的封裝類型。下面以Integer與int之間的轉換做爲理解:

3.1 自動裝箱(Autoboxing)

Integer a = 2; //Boxing

複製代碼

簡單的理解:將2裝在一個箱子裏,這個箱子的類型是Integer 。箱子這裏面裝的數值就是2,咱們就完成了一次裝箱操做。並把a指向2這個箱子。

Integer b = new Integer(2);//Boxing

複製代碼

顯示裝箱。生成一個新的箱子 new Integer(); 而且這個箱子的值爲2.並且讓b指向這個箱子。

3.2 拆箱(Unboxing)

故名思議就是將對象從新轉化爲基本數據類型

int v = a.intValue(); //Unboxing

複製代碼

簡單的理解:將裏面int的值取出來。拆箱有個很典型的用法就是在進行運算的時候:由於對象時不能直接進行運算的,而是要轉化爲基本數據類型後才能進行加減乘除。

例如:

Integer c = 5;
System.out.print(c--);//進行計算時隱含的有自動拆箱

複製代碼

4. 裝箱拆箱結合源碼分析

經過第四點咱們知道裝箱拆箱的基本概念知識,下面咱們一樣以Integer 爲例,進入源碼裏面看看裏面的乾坤。

咱們首先看下Integer的大小。

4.1 Integer 大小

能夠看出,其定義了Integer的最大值爲2^31-1,最小值爲-2^31。Integer的基本數據類型爲int。

Integer大小

4.2 Integer中的valueOf()方法

再來看看Integer中的valueOf()方法。

Integer中的valueOf()

能夠看出valueOf()方法是個靜態方法。當傳進來的變量值在一個區間以內,直接用IntegerCache.cache[]數組裏面的數返回,不然new一個新對象。

接着咱們來看看IntegerCache類。其實也是會出現坑的一個地方。

4.3 其中存在的陷阱

接着來講下Integer這兒的一個坑,也是比較有意思的地方。

IntegerCache

初始化Integer後,IntegerCache會緩存[-128,127]之間的數據,這個區間的上限能夠配置,取決於java.lang.Integer.IntegerCache.high這個屬性,這個屬性在VM參數裏爲-XX:AutoBoxCacheMax=2000進行設置調整或者VM裏設置-Djava.lang.Integer.IntegerCache.high=2000。因此Integer在初始化完成後會緩存[-128,max]之間的數據。cache屬於常量,存放在java的方法區中。

一樣,在Long,Byte,Short,咱們也能夠看到緩存,其緩存數據長度均是-128到127。這裏不作展開。

另外其餘陷阱: 如:

System.out.println(Integer.valueOf(null));

複製代碼

Integer對象的值能夠爲null,因此編譯器檢查時不會出現檢查時異常,可是在轉換成int的時候就會拋出空指針異常。

4. 例題分析

咱們經過幾個經典的問題,來看看你們到底理解了裝箱與拆箱的知識點沒。

  • new Integer(5) == 5?
  • new Integer(5) == new Integer(5) ?
  • Integer.valueOf(5) == Integer.valueOf(5)?
  • Integer.valueOf(5).intValue() == 5?
  • new Integer(5).equals(new Integer(5))?

4.1 問題一:new Integer(5) == 5?

答案:true。 等號的左邊是一個Object右邊是一個數值,Object和數值怎麼會相等的呢?Java的編譯器很聰明,它會本身去作裝箱和拆箱的操做。這邊它將new Integer(5)作的是Unboxing,它會裏面的value取出來,這時候發現取出來的5等於右邊,因此就爲true。

4.2 問題二:new Integer(5) == new Integer(5) ?

答案:false。 new Integer(5) 就是新建一個箱子,這個箱子的值就是5。 == 是判斷這兩個箱子是否是同一個箱子,不是說裏面的值是否是同樣.因此是false。由於他們不是同一個箱子。

4.3 問題三:Integer.valueOf(5) == Integer.valueOf(5)?

答案: true。 Integer.valueOf(5)它會返回一個箱子給咱們,箱子裏面的值是5。可是在返回這個箱子給咱們的時候,可能會新建一個新的箱子給咱們,也可能會使用現有的一個箱子給咱們。因此Integer.valueOf(5) == Integer.valueOf(5)。什麼狀況下才會相等呢?只有當系統已經將2這個箱子創建好了,而且緩存起來的狀況下。會把箱子的引用同時發給等號的左邊與右邊。這樣的狀況,他們纔會互相相等。Integer.valueOf() 是系統給咱們分配的一個箱子,咱們發現,每次調咱們的箱子時候,系統都給了同一個箱子。這個咱們的 Integer.valueOf(5) == Integer.valueOf(5)

可是: 可能爲false。咱們在上面介紹過,在low和high之間,它會返回一個系統已經生產的cache,不然它會生產一個新的出來。看源碼能夠看到low = -128 high = 127。因此當它的值超過了區間後,它就會返回新的箱子,因此就會爲false。

咱們不用5改用200試一試。

Integer.valueOf(200) == Integer.valueOf(200)

複製代碼

答案:false。 說明系統對小的數字會使用系統分配的箱子,對於大的數字,系統會從新new一個箱子。面試的時候,能夠回答,他們可能相等,也可能不相等。是有系統決定的。

4.4 問題四:Integer.valueOf(5).intValue() == 5?

答案: true。 intValue()作了一個拆箱的操做,將裏面的值5取出來,值5等於5,因此是true。

4.5 問題五:new Integer(5).equals(new Integer(5))?

答案:true。 這裏咱們沒有用==而是用equals,equals判斷相等是判斷裏面的值是否是相等,而不是判斷這個箱子是否是同一個,因此咱們的答案是true。咱們來看看equals的源碼。判斷裏面的值是否是相等。

equals

打印結果:

打印結果

文末

本章節主要簡單介紹了自動裝箱與拆箱的相關知識,但願對你們有所幫助~ 從此我會在每張文章開頭增長 每章一點正能量 ,文末增長5個編程相關的英語單詞 學點英語。但願你們和我同樣天天都能積極向上,一塊兒學習一同進步!

學點英語

  • AWT(Abstract Window Toolkit)抽象窗口工具
  • API(Application Programming Interface)應用程序接口
  • AOP Aspect Oriented Programming(面向切面編程),能夠 經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一 添加功能的一種技術。
  • BMP Bean-Managed Persistent(Bean管理的持久性),EJB中由 Bean本身負責持久性管理的方法,Bean的內容的同步(保存)須要本身編寫代碼 實現。
  • I18N internationalization(國際化),這個單詞的長度是20,而後取 其首尾字母,中間省略的字母恰好18個。

歡迎關注公衆號:Coder編程 獲取最新原創技術文章和相關免費學習資料,隨時隨地學習技術知識!

參考文章:

blog.csdn.net/u013309870/…

blog.csdn.net/jairuschan/…

www.cnblogs.com/dolphin0520…

微信公衆號

推薦閱讀

一篇帶你讀懂TCP之「滑動窗口」協議

帶你瞭解數據庫中JOIN的用法

帶你瞭解數據庫中group by的用法

相關文章
相關標籤/搜索