int 和 integer :裝箱和拆箱的過程,會用到什麼方法,你覺得這個會對性能有影響嗎,原因是什麼(百度一面)

2021-02-19 java知路

點擊上方「五角錢的程式設計師」,選擇「設為星標」

✨一起學習、成長、溫情的熱愛生活✨  

今天繼續來講面試,已經出了很多java一面真題系列文章了,之後也會整理成一個系列,歡迎持續關注哦。

話說幹了這麼多年的開發,只知道會用,怎麼用,用什麼,隱約也知道了為什麼用,但為啥JAVA總像一個猶抱琵琶半遮面的女子,總讓人看不透,看不膩?應該就是基礎不紮實了。所以面試的時候卻經常會被問到,我們根據面經來進行補短板,查漏補缺。下面開始今天的乾貨內容吧,走起,記得點讚,點擊在看哦。

自動裝箱和拆箱從Java 1.5開始引入,目的是將原始類型值轉自動地轉換成對應的對象。自動裝箱與拆箱的機制可以讓我們在Java的變量賦值或者是方法調用等情況下使用原始類型或者對象類型更加簡單直接。如果你在Java1.5下進行過編程的話,你一定不會陌生這一點,你不能直接地向集合(Collections)中放入原始類型值,因為集合只接收對象。通常這種情況下你的做法是,將這些原始類型的值轉換成對象,然後將這些轉換的對象放入集合中。使用Integer,Double,Boolean等這些類我們可以將原始類型值轉換成對應的對象,但是從某些程度可能使得代碼不是那麼簡潔精煉。為了讓代碼簡練,Java 1.5引入了具有在原始類型和對象類型自動轉換的裝箱和拆箱機制。但是自動裝箱和拆箱並非完美,在使用時需要有一些注意事項,如果沒有搞明白自動裝箱和拆箱,可能會引起難以察覺的bug。1、包裝類型Java語言是一個面向對象的語言,但是Java中的基本數據類型卻是不面向對象的,這在實際使用時存在很多的不便,為了解決這個不足,在設計類時為每個基本數據類型設計了一個對應的類進行代表,這樣八個和基本數據類型對應的類統稱為包裝類(Wrapper Class)。包裝類均位於java.lang包,包裝類和基本數據類型的對應關係如下表所示
在這八個類名中,除了Integer和Character類以後,其它六個類的類名和基本數據類型一致,只是類名的第一個字母大寫即可。
2、為什麼需要包裝類很多人會有疑問,既然Java中為了提高效率,提供了八種基本數據類型,為什麼還要提供包裝類呢?這個問題,其實前面已經有了答案,因為Java是一種面向對象語言,很多地方都需要使用對象而不是基本數據類型。比如,在集合類中,我們是無法將int 、double等類型放進去的。因為集合的容器要求元素是Object類型。為了讓基本類型也具有對象的特徵,就出現了包裝類型,它相當於將基本類型「包裝起來」,使得它具有了對象的性質,並且為其添加了屬性和方法,豐富了基本類型的操作。3、拆箱與裝箱在Java SE5之前,要進行裝箱,可以通過以下代碼:

Integer i = new Integer(10);


4、自動拆箱與自動裝箱

自動裝箱就是Java自動將原始類型值轉換成對應的對象,比如將int的變量轉換成Integer對象,這個過程叫做裝箱,反之將Integer對象轉換成int類型值,這個過程叫做拆箱。因為這裡的裝箱和拆箱是自動進行的非人為轉換,所以就稱作為自動裝箱和拆箱。

在Java SE5中,為了減少開發人員的工作,Java提供了自動拆箱與自動裝箱功能。自動裝箱: 就是將基本數據類型自動轉換成對應的包裝類。自動拆箱:就是將包裝類自動轉換成對應的基本數據類型。

Integer i =10;  //自動裝箱
int b= i;     //自動拆箱

Integer i=10 可以替代 Integer i = new Integer(10);,這就是因為Java幫我們提供了自動裝箱的功能,不需要開發者手動去new一個Integer對象。5、自動裝箱與自動拆箱的實現原理既然Java提供了自動拆裝箱的能力,那麼,我們就來看一下,到底是什麼原理,Java是如何實現的自動拆裝箱功能。

public static  void main(String[]args){
    Integer integer=1; //裝箱
    int i=integer; //拆箱
}

public static  void main(String[]args){
    Integer integer=Integer.valueOf(1); 
    int i=integer.intValue(); 
}

從上面反編譯後的代碼可以看出,int的自動裝箱都是通過Integer.valueOf()方法來實現的,Integer的自動拆箱都是通過integer.intValue來實現的。如果讀者感興趣,可以試著將八種類型都反編譯一遍 ,你會發現以下規律:自動裝箱都是通過包裝類的valueOf()方法來實現的.自動拆箱都是通過包裝類對象的xxxValue()來實現的。6、哪些地方會自動拆裝箱我們了解過原理之後,在來看一下,什麼情況下,Java會幫我們進行自動拆裝箱。前面提到的變量的初始化和賦值的場景就不介紹了,那是最簡單的也最容易理解的。場景一、將基本數據類型放入集合類我們知道,Java中的集合類只能接收對象類型,那麼以下代碼為什麼會不報錯呢?

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i ++){
    li.add(i);
}

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2){
    li.add(Integer.valueOf(i));
}

以上,我們可以得出結論,當我們把基本數據類型放入集合類中的時候,會進行自動裝箱。場景二、包裝類型和基本類型的大小比較有沒有人想過,當我們對Integer對象與基本類型進行大小比較的時候,實際上比較的是什麼內容呢?看以下代碼:

Integer a=1;
System.out.println(a==1?"等於":"不等於");
Boolean bool=false;
System.out.println(bool?"真":"假");

Integer a=1;
System.out.println(a.intValue()==1?"等於":"不等於");
Boolean bool=false;
System.out.println(bool.booleanValue?"真":"假");

可以看到,包裝類與基本數據類型進行比較運算,是先將包裝類進行拆箱成基本數據類型,然後進行比較的。場景三、包裝類型的運算有沒有人想過,當我們對Integer對象進行四則運算的時候,是如何進行的呢?看以下代碼:

Integer i = 10;
Integer j = 20;

System.out.println(i+j);

Integer i = Integer.valueOf(10);
Integer j = Integer.valueOf(20);
System.out.println(i.intValue() + j.intValue());

我們發現,兩個包裝類型之間的運算,會被自動拆箱成基本類型進行。場景四、三目運算符的使用這是很多人不知道的一個場景,作者也是一次線上的血淋淋的Bug發生後才了解到的一種案例。看一個簡單的三目運算符的代碼:

boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j;

很多人不知道,其實在int k = flag ? i : j;這一行,會發生自動拆箱。反編譯後代碼如下:

boolean flag = true;
Integer i = Integer.valueOf(0);
int j = 1;
int k = flag ? i.intValue() : j;

這其實是三目運算符的語法規範:當第二,第三位操作數分別為基本類型和對象時,其中的對象就會拆箱為基本類型進行操作。因為例子中,flag ? i : j;片段中,第二段的i是一個包裝類型的對象,而第三段的j是一個基本類型,所以會對包裝類進行自動拆箱。如果這個時候i的值為null,那麼久會發生NPE。(自動拆箱導致空指針異常)場景五、函數參數與返回值

//自動拆箱
public int getNum1(Integer num) {
 return num;
}
//自動裝箱
public Integer getNum2(int num) {
 return num;
}

7、自動拆裝箱與緩存Java SE的自動拆裝箱還提供了一個和緩存有關的功能,我們先來看以下代碼,猜測一下輸出結果:

public static void main(String... strings) {

    Integer integer1 = 3;
    Integer integer2 = 3;

    if (integer1 == integer2)
        System.out.println("integer1 == integer2");
    else
        System.out.println("integer1 != integer2");

    Integer integer3 = 300;
    Integer integer4 = 300;

    if (integer3 == integer4)
        System.out.println("integer3 == integer4");
    else
        System.out.println("integer3 != integer4");

}

我們普遍認為上面的兩個判斷的結果都是false。雖然比較的值是相等的,但是由於比較的是對象,而對象的引用不一樣,所以會認為兩個if判斷都是false的。在Java中,==比較的是對象應用,而equals比較的是值。所以,在這個例子中,不同的對象有不同的引用,所以在進行比較的時候都將返回false。奇怪的是,這裡兩個類似的if條件判斷返回不同的布爾值。

integer1 == integer2
integer3 != integer4

原因就和Integer中的緩存機制有關。在Java 5中,在Integer的操作上引入了一個新功能來節省內存和提高性能。整型對象通過使用相同的對象引用實現了緩存和重用。具體的代碼實現可以閱讀Java中整型的緩存機制一文,這裡不再闡述。我們只需要知道,當需要進行自動裝箱時,如果數字在-128至127之間時,會直接使用緩存中的對象,而不是重新創建一個對象。其中的javadoc詳細的說明了緩存支持-128到127之間的自動裝箱過程。最大值127可以通過-XX:AutoBoxCacheMax=size修改。實際上這個功能在Java 5中引入的時候,範圍是固定的-128 至 +127。後來在Java 6中,可以通過java.lang.Integer.IntegerCache.high設置最大值。這使我們可以根據應用程式的實際情況靈活地調整來提高性能。到底是什麼原因選擇這個-128到127範圍呢?因為這個範圍的數字是最被廣泛使用的。在程序中,第一次使用Integer的時候也需要一定的額外時間來初始化這個緩存。在Boxing Conversion部分的Java語言規範(JLS)規定如下:true 和 false的布爾值 (§3.10.3)『\u0000』至 『\u007f』之間的字符(§3.10.4)
範圍內的時,將p包裝成a和b兩個對象時,可以直接使用a==b判斷a和b的值是否相等。8、自動拆裝箱帶來的問題當然,自動拆裝箱是一個很好的功能,大大節省了開發人員的精力,不再需要關心到底什麼時候需要拆裝箱。但是,他也會引入一些問題。包裝對象的數值比較,不能簡單的使用==,雖然-128到127之間的數字可以,但是這個範圍之外還是需要使用equals比較。前面提到,有些場景會進行自動拆裝箱,同時也說過,由於自動拆箱,如果包裝類對象為null,那麼自動拆箱時就有可能拋出NPE。如果一個for循環中有大量拆裝箱操作,會浪費很多資源。
常見筆試題:

Integer i1 =59;
int i2 = 59;
Integer i3 = Integer.valueOf(59);
Integer i4 = new Integer(59);
System.out.println(i1 == i2); true:包裝類和基本類型比較時,包裝類自動拆箱為基本類型
System.out.println(i1 == i3); true:數值59 在-128到127之間,上文所說的緩存,因此為true,若數值不在-128到127之間則為false
System.out.println(i1 == i4); false:引用類型比較地址值,地址值不同
System.out.println(i2 == i3); true:i1的源碼是i3,i2和i3比較結果和i2與i1比較結果相同,包裝類和基本類型比較時自動拆箱
System.out.println(i2 == i4); true:包裝類和基本類型比較時自動拆箱

System.out.println(i3 == i4);同i1 == i4

9、自動拆箱和裝箱會影響性能裝箱:原始類型轉化為包裝類型;如Integer i = 0;java語法默認數值類型為int,0原始類型,i為包裝類型,兩種不同類型為什麼可以賦值呢,這就是因為java做了個自動裝箱的操作;拆箱則相反。


上圖l = l +1;l+1中l自動拆箱了,所以和是原始類型,後面賦值,和又自動裝箱了。不過通過上圖發現,好像循環次數少的時候,jvm處理的很快,是不是不用優化了?答案是不是,因為你的程序不止一個人在使用,當很多人調用的時候,積塔成沙,就會很消耗性能。我們減少這種拆箱和裝箱,看看優化的效果:


優化後,l  + 1裡l進行拆箱,發現少了一次自動裝箱,對性能影響這麼大,還覺得減少自動拆箱和自動裝箱過程沒用嗎?優化從點滴做起。

由此可見,裝箱和拆箱/拷貝操作會從速度和內存兩個方面損傷應用程式的性能。因此我們應該清楚編譯器會在何時自動產生執行這些操作的指令,並使我們編寫的代碼儘可能減少導致這種情況發生的機會。

所有巧合的是要麼是上天註定要麼是一個人偷偷的在努力。

參考連結:https://www.cnblogs.com/cn-chy-com/p/10816565.html

                    https://www.cnblogs.com/lsohvaen001/p/7823067.html

相關焦點

  • int 和 integer:裝箱和拆箱的過程,會用到什麼方法
    本文轉載自【微信公眾號:五角錢的程式設計師,ID:xianglin965】,經微信公眾號授權轉載,如需轉載與原文作者聯繫原文標題《int 和 integer :裝箱和拆箱的過程,會用到什麼方法,你覺得這個會對性能有影響嗎,原因是什麼(百度一面)》圖丨pixabay
  • java面試技巧:Integer和int的那些事
    最近在招聘試的過程中,考察些候選的基礎掌握能中發現,還是有多數了有1~3年的開發者在基礎這塊掌握的不夠牢靠,沒有去思考過為什麼這樣做,以及這樣做的原因是什麼?那麼今天我們就來聊聊Java中的Integer和int,以及他們在試中般會如何考候選呢?
  • 自學C井之路7——裝箱和拆箱
    我將再一次開始自學C#,並每日記錄自學過程。以此督促自己堅持學習。同時也希望看到這篇文章的讀者不吝賜教。C#中的變量分為值類型和引用類型兩種。值類型變量存儲在棧上,比較簡單;引用型變量存儲在堆上,比較複雜。
  • int和Integer裝箱拆箱遇見的坑
    前言:Java是一個近乎純潔的面向對象程式語言,但是為了編程的方便還是引入了基本數據類型,但是為了能夠將這些基本數據類型當成對象操作,Java為每一個基本數據類型都引入了對應的包裝類型(wrapper class),int的包裝類就是Integer,從Java 5開始引入了自動裝箱/拆箱機制,
  • Integer,int,==解析
    Integer a = 127;Integer b = new Integer(127);System.out.println(a==b);//falseInteger a =127;這裡有一個自動裝箱的過程,本質是調用Integer.valueof(127);而Integer b = new Integer(127);新建了一個對象。所以不相等。
  • 面試官:詳細說下基本數據類型與裝箱拆箱
    一:在方法中聲明的變量,即該變量是局部變量,每當程序調用方法時,系統都會為該方法建立一個方法棧,其所在方法中聲明的變量就放在方法棧中,當方法結束系統會釋放方法棧,其對應在該方法中聲明的變量隨著棧的銷毀而結束,這就局部變量只能在方法中有效的原因 在方法中聲明的變量可以是基本類型的變量,也可以是引用類型的變量
  • 聽說Integer有bug?1000不等於1000?
    前幾天有位朋友找我,說:「老哥,老哥,我好像發現了Integer一個bug,你幫我看看什麼情況?」,說完給了我兩個很簡單的demo,上代碼。100 == 1001000 == 1000通過代碼,我們可以看到,這是很簡單的「100==100」、「1000==1000」,但是為什麼一個是「true」,一個是「false」,難道真的是bug?
  • C#提升性能的幾點提示和技巧
    在我的測試中,此循環使性能提高了8倍!注意區別嗎?這是我們遍歷此數組數組的順序([i] [n]與[n] [i])。即使我們從自己管理內存中抽象出來,內存局部性在.NET中的確很重要。就我而言,這種方法被稱為數百萬次(準確地說是數億次),因此我可以從中獲得的任何性能都獲得了可觀的勝利。再次感謝我經常使用的分析器,以確保我專注於正確的地方!
  • Integer 這個類型你真的了解嗎?
    Java中基本數據類型和包裝類這張圖相信大家在初學Java的時候都應該記得很熟了,包括jdk1.5之後引入的裝箱和拆箱那就更沒有什麼問題了,大家都知道Java是面向對象的,而基本類型數據不帶有其他信息或者可操作方法。這在實際使用中存在很多不足,為了解決這個不足而引入了引用類型。
  • Integer引發的生產問題
    我們有個Batch的實體類,裡面有個Integer的id 成員變量,這個是資料庫自增欄位,具體代碼如下:其實這裡面有兩個問題:1、方法聲明是Integer類型,傳入一個基本類型怎麼也可以?2,對象的比較==用法問題?先看第一個問題,第一個問題就是Java 在Java SE5引入的自動裝箱和自動拆箱功能。自動裝箱和自動拆箱裝箱就是自動將基本數據類型轉換為包裝器類型;拆箱就是自動將包裝器類型轉換為基本數據類型。
  • 詳解.NET中六個你必須知道的重要概念
    public void Method1()  {   int i=4;   int y=2;   class1 cls1 = new class1();  } 這個方法內部只有三行代碼,下面我就逐行解釋內部發生了什麼事情。第一行:執行該行時,編譯器分配一小塊叫做堆棧的內存,堆棧負責保持跟蹤應用程式運行需要的內存。
  • 不了解這 12 個語法糖,別說你會 Java!
    如果你去看com.sun.tools.javac.main.JavaCompiler的源碼,你會發現在compile()中有一個步驟就是調用desugar(),這個方法就是負責解語法糖的實現的。Java 中最常用的語法糖主要有泛型、變長參數、條件編譯、自動拆裝箱、內部類等。本文主要來分析下這些語法糖背後的原理。一步一步剝去糖衣,看看其本質。
  • int和Integer的面試題,你都會了嗎
    private final int value;int和Integer的對比,經常被拿來作為面試題,下面就多種情況,通過源碼分析其結果。我們將比較「==」和Equals兩種結果。第一種情況:這種情況是通過new關鍵字創建Integer對象,參數值一樣。內部的int類型的value變量會保存參數值。
  • 面試官:這麼基礎的題目都做錯,都不知道你是怎麼做項目
    一.什麼是裝箱?什麼是拆箱?在程序中,如果我們需要生成一個數值的對象,可以這樣使用Integer a = 100; //裝箱這個過程就會根據數值創建對應的 Integer 對象這就是裝箱而拆箱呢,則可以這樣
  • 不懂這12個語法糖,別說你會Java!
    如果你去看com.sun.tools.javac.main.JavaCompiler的源碼,你會發現在compile()中有一個步驟就是調用desugar(),這個方法就是負責解語法糖的實現的。Java 中最常用的語法糖主要有泛型、變長參數、條件編譯、自動拆裝箱、內部類等。本文主要來分析下這些語法糖背後的原理。一步一步剝去糖衣,看看其本質。
  • 不了解這12個語法糖,別說你會Java!
    如果你去看com.sun.tools.javac.main.JavaCompiler的源碼,你會發現在compile()中有一個步驟就是調用desugar(),這個方法就是負責解語法糖的實現的。Java 中最常用的語法糖主要有泛型、變長參數、條件編譯、自動拆裝箱、內部類等。本文主要來分析下這些語法糖背後的原理。一步一步剝去糖衣,看看其本質。
  • ==與equlas您懂了嗎?
    == 之間的比較一):int 與 Integer、 new Integer 比較分析:因為包裝類Integer 和 基本數據類型int 比較時,java會自動拆箱為int ,然後進行比較,實際上就變為兩個int變量的比較。