最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談Java自動裝箱與拆箱及其陷阱

 更新時間:2016年06月12日 12:25:44   投稿:jingxian  
下面小編就為大家?guī)硪黄獪\談Java自動裝箱與拆箱及其陷阱。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

在本文中,筆者向大家介紹下Java中一個非常重要也非常有趣的特性,就是自動裝箱與拆箱,并從源碼中解讀自動裝箱與拆箱的原理,同時這種特性也留有一個陷阱。開發(fā)者如果不注意,就會很容易跌入這個陷阱。

自動裝箱(Autoboxing)

定義

大家在平時編寫Java程序時,都常常以以下方式來定義一個Integer對象:

Integer i=100;

從上面的代碼中,大家可以得知,i為一個Integer類型的引用,100為Java中的基礎(chǔ)數(shù)據(jù)類型(primitive data type)。而這種直接將一個基礎(chǔ)數(shù)據(jù)類型傳給其相應(yīng)的封裝類(wrapper class)的做法,便是自動裝箱(Autoboxing)。

在jdk 1.5中,自動裝箱首次被引入。而在jdk 1.5之前,如果你想要定義一個value為100的Integer對象,則需要這樣做:

Integer i=new Integer (100);

原理

我們在以上代碼“Integer i=100;”處打一個斷點,跟蹤一下。

           

接下來,我們可以看到,程序跳轉(zhuǎn)到了Integer類的valueOf(int i)方法中

/**
   * Returns a <tt>Integer</tt> instance representing the specified
   * <tt>int</tt> value.
   * If a new <tt>Integer</tt> instance is not required, this method
   * should generally be used in preference to the constructor
   * {@link #Integer(int)}, as this method is likely to yield
   * significantly better space and time performance by caching
   * frequently requested values.
   *
   * @param i an <code>int</code> value.
   * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
   * @since 1.5
   */
  public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
      return IntegerCache.cache[i + 128];
    else
      return new Integer(i);
  }

換句話說,裝箱就是jdk自己幫你完成了調(diào)用Integer.valueOf(100)。

拆箱(Unboxing)

定義

Integer integer100=100;
int int100=integer100;

從上面的代碼中,大家可看出integer100為一個Integer類型的引用,int100為一個int類型的原始數(shù)據(jù)類型。但是,我們可以將一個Integer類型的對象賦值給其相應(yīng)原始數(shù)據(jù)類型的變量。這便是拆箱。

拆箱與裝箱是相反的操作。裝箱是將一個原始數(shù)據(jù)類型賦值給相應(yīng)封裝類的變量。而拆箱則是將一個封裝類的變量賦值給相應(yīng)原始數(shù)據(jù)類型的變量。裝箱、拆箱的名字也取得相當(dāng)貼切。

原理

筆者相信大家也都猜到了,拆箱過程中jdk為我們做了什么。我們還是通過實驗來證明我們的猜想吧。

在以上代碼的第二行代碼打上斷點,即在“int int100=integer100;”上打上斷點,跟蹤一下。

我們可以看到,程序跳轉(zhuǎn)到了Integer的intValue()方法。

/**
   * Returns the value of this <code>Integer</code> as an
   * <code>int</code>.
   */
  public int intValue() {
	return value;
  }

也就是,jdk幫我們完成了對intValue()方法的調(diào)用。對于以上的實驗而言,便是調(diào)用integer100的intValue()方法,將其返回值賦給了int100。

擴(kuò)展

實驗1

Integer integer400=400;
int int400=400;
System.out.println(integer400==int400);

在以上代碼的第三行中,integer400與int400執(zhí)行了==運行。而這兩個是不同類型的變量,到底是integer400拆箱了,還是int400裝箱了呢?運行結(jié)果是什么呢?

 ==運算是判斷兩個對象的地址是否相等或者判斷兩個基礎(chǔ)數(shù)據(jù)類型的值是否相等。所以,大家很容易推測到,如果integer400拆箱了,則說明對比的是兩個基礎(chǔ)類型的值,那此時必然相等,運行結(jié)果為true;如果int400裝箱了,則說明對比的是兩個對象的地址是否相等,那此時地址必然不相等,運行結(jié)果為false。(至于為什么筆者對它們賦值為400,就是后面將要講到的陷阱有關(guān))。

我們實際的運行結(jié)果為true。所以是integer400拆箱了。對代碼跟蹤的結(jié)果也證明這一點。

實驗2

Integer integer100=100;
int int100=100;
System.out.println(integer100.equals(int100));

在以上代碼的第三行中,integer100的方法equals的參數(shù)為int100。我們知道equals方法的參數(shù)為Object,而不是基礎(chǔ)數(shù)據(jù)類型,因而在這里必然是int100裝箱了。對代碼跟蹤的結(jié)果也證明了這一點。

其實,如果一個方法中參數(shù)類型為原始數(shù)據(jù)類型,所傳入的參數(shù)類型為其封裝類,則會自動對其進(jìn)行拆箱;相應(yīng)地,如果一個方法中參數(shù)類型為封裝類型,所傳入的參數(shù)類型為其原始數(shù)據(jù)類型,則會自動對其進(jìn)行裝箱。

實驗3

Integer integer100 = 100;
int int100 = 100;
Long long200 = 200l;
System.out.println(integer100 + int100);
System.out.println(long200 == (integer100 + int100));
System.out.println(long200.equals(integer100 + int100));

在第一個實驗中,我們已經(jīng)得知,當(dāng)一個基礎(chǔ)數(shù)據(jù)類型與封裝類進(jìn)行==運算時,會將封裝類進(jìn)行拆箱。那如果+、-、*、/呢?我們在這個實驗中,就可知道。

如果+運算,會將基礎(chǔ)數(shù)據(jù)類型裝箱,那么:

•第4行中,integer100+int100就會得到一個類型為Integer且value為200的對象o,并執(zhí)行這個對象的toString()方法,并輸出”200”;

•第5行中,integer100+int100就會得到一個類型為Integer且value為200的對象o,==運算將這個對象與long200對象進(jìn)行對比,顯然,將會輸出false;

•第6行中,integer100+int100就會得到一個類型為Integer且value為200的對象o,Long的equals方法將long200與o對比,因為兩都是不同類型的封裝類,因而輸出false;

如果+運算,會將封裝類進(jìn)行拆箱,那么:

•第4行中,integer100+int100就會得到一個類型為int且value為200的基礎(chǔ)數(shù)據(jù)類型b,再將b進(jìn)行裝箱得到o,執(zhí)行這個對象的toString()方法,并輸出”200”;

•第5行中,integer100+int100就會得到一個類型為int且value為200的基礎(chǔ)數(shù)據(jù)類型b1,==運算將long200進(jìn)行拆箱得到b2,顯然b1==b2,輸出true;

•第6行中,integer100+int100就會得到一個類型為int且value為200的基礎(chǔ)數(shù)據(jù)類型b,Long的equals方法將b進(jìn)行裝箱,但裝箱所得到的是類型為Integer的對象o,因為o與long200為不同的類型的對象,所以輸出false;

程序運行的結(jié)果為:     

200
true
false

因而,第二種推測是正確,即在+運算時,會將封裝類進(jìn)行拆箱。

陷阱

陷阱1

Integer integer100=null;
int int100=integer100;

這兩行代碼是完全合法的,完全能夠通過編譯的,但是在運行時,就會拋出空指針異常。其中,integer100為Integer類型的對象,它當(dāng)然可以指向null。但在第二行時,就會對integer100進(jìn)行拆箱,也就是對一個null對象執(zhí)行intValue()方法,當(dāng)然會拋出空指針異常。所以,有拆箱操作時一定要特別注意封裝類對象是否為null。

陷阱2

Integer i1=100;
Integer i2=100;
Integer i3=300;
Integer i4=300;
System.out.println(i1==i2);
System.out.println(i3==i4);

因為i1、i2、i3、i4都是Integer類型的,所以我們想,運行結(jié)果應(yīng)該都是false。但是,真實的運行結(jié)果為“System.out.println(i1==i2);”為 true,但是“System.out.println(i3==i4);”為false。也就意味著,i1與i2這兩個Integer類型的引用指向了同一個對象,而i3與i4指向了不同的對象。為什么呢?不都是調(diào)用Integer.valueOf(int i)方法嗎?

讓我們再看看Integer.valueOf(int i)方法。

/**
   * Returns a <tt>Integer</tt> instance representing the specified
   * <tt>int</tt> value.
   * If a new <tt>Integer</tt> instance is not required, this method
   * should generally be used in preference to the constructor
   * {@link #Integer(int)}, as this method is likely to yield
   * significantly better space and time performance by caching
   * frequently requested values.
   *
   * @param i an <code>int</code> value.
   * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
   * @since 1.5
   */
  public static Integer valueOf(int i) {
    if(i >= -128 && i <= IntegerCache.high)
      return IntegerCache.cache[i + 128];
    else
      return new Integer(i);
  }

我們可以看到當(dāng)i>=-128且i<=IntegerCache.high時,直接返回IntegerCache.cache[i + 128]。其中,IntegerCache為Integer的內(nèi)部靜態(tài)類,其原碼如下:

private static class IntegerCache {
    static final int high;
    static final Integer cache[];

    static {
      final int low = -128;

      // high value may be configured by property
      int h = 127;
      if (integerCacheHighPropValue != null) {
        // Use Long.decode here to avoid invoking methods that
        // require Integer's autoboxing cache to be initialized
        int i = Long.decode(integerCacheHighPropValue).intValue();
        i = Math.max(i, 127);
        // Maximum array size is Integer.MAX_VALUE
        h = Math.min(i, Integer.MAX_VALUE - -low);
      }
      high = h;

      cache = new Integer[(high - low) + 1];
      int j = low;
      for(int k = 0; k < cache.length; k++)
        cache[k] = new Integer(j++);
    }

    private IntegerCache() {}
  }

我們可以清楚地看到,IntegerCache有靜態(tài)成員變量cache,為一個擁有256個元素的數(shù)組。在IntegerCache中也對cache進(jìn)行了初始化,即第i個元素是值為i-128的Integer對象。而-128至127是最常用的Integer對象,這樣的做法也在很大程度上提高了性能。也正因為如此,“Integeri1=100;Integer i2=100;”,i1與i2得到是相同的對象。

對比擴(kuò)展中的第二個實驗,我們得知,當(dāng)封裝類與基礎(chǔ)類型進(jìn)行==運行時,封裝類會進(jìn)行拆箱,拆箱結(jié)果與基礎(chǔ)類型對比值;而兩個封裝類進(jìn)行==運行時,與其它的對象進(jìn)行==運行一樣,對比兩個對象的地址,也即判斷是否兩個引用是否指向同一個對象。

以上這篇淺談Java自動裝箱與拆箱及其陷阱就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springcloud如何使用dubbo開發(fā)rpc服務(wù)及調(diào)用

    springcloud如何使用dubbo開發(fā)rpc服務(wù)及調(diào)用

    這篇文章主要介紹了springcloud如何使用dubbo開發(fā)rpc服務(wù)及調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • springboot將lib和jar分離的操作方法

    springboot將lib和jar分離的操作方法

    本文介紹了如何通過優(yōu)化pom.xml配置來減小Spring Boot項目的jar包大小,主要通過使用spring-boot-maven-plugin和maven-dependency-plugin插件,將依賴庫打包到j(luò)ar中,并通過指定外部lib路徑的方式運行jar,從而減小jar包體積,感興趣的朋友一起看看吧
    2025-02-02
  • SpringBoot圖文并茂講解Lombok庫的安裝與使用

    SpringBoot圖文并茂講解Lombok庫的安裝與使用

    Lombok想要解決了的是在我們實體Bean中大量的Getter/Setter方法,以及toString, hashCode等可能不會用到,但是某些時候仍然需要復(fù)寫,以期方便使用的方法;在使用Lombok之后,將由其來自動幫你實現(xiàn)代碼生成
    2022-06-06
  • SpringMVC JSON數(shù)據(jù)交互實現(xiàn)過程解析

    SpringMVC JSON數(shù)據(jù)交互實現(xiàn)過程解析

    這篇文章主要介紹了SpringMVC JSON數(shù)據(jù)交互實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • WebSocket獲取httpSession空指針異常的解決辦法

    WebSocket獲取httpSession空指針異常的解決辦法

    這篇文章主要介紹了在使用WebSocket實現(xiàn)p2p或一對多聊天功能時,如何獲取HttpSession來獲取用戶信息,本文結(jié)合實例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-01-01
  • Java項目中統(tǒng)計代碼耗時的工具類

    Java項目中統(tǒng)計代碼耗時的工具類

    相信大家在實際工具中,經(jīng)常需要做性能分析,做性能分析就少不了代碼執(zhí)行的耗時分析,本文將使用Java編寫一個項目統(tǒng)計代碼耗時的工具類,希望對大家有所幫助
    2025-08-08
  • springboot2.3 整合mybatis-plus 高級功能及用法詳解

    springboot2.3 整合mybatis-plus 高級功能及用法詳解

    這篇文章主要介紹了springboot2.3 整合mybatis-plus 高級功能,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 一文帶你了解Java中的ForkJoin

    一文帶你了解Java中的ForkJoin

    這篇文章主要介紹了一文帶你了解Java中的ForkJoin,F(xiàn)orkJoinTask本身的依賴關(guān)系并不復(fù)雜,它與異步任務(wù)計算FutureTask一樣均實現(xiàn)了Future接口,下文更多相關(guān)資料,需要的小伙伴可以參考一下
    2022-04-04
  • Java虛擬機執(zhí)行引擎知識總結(jié)

    Java虛擬機執(zhí)行引擎知識總結(jié)

    這篇文章主要介紹了有關(guān)Java虛擬機執(zhí)行引擎的知識,文中實例簡單易懂,方便大家更好的學(xué)習(xí),有興趣的朋友可以了解下
    2020-06-06
  • springboot中thymeleaf模板使用詳解

    springboot中thymeleaf模板使用詳解

    這篇文章將更加全面詳細(xì)的介紹thymeleaf的使用。thymeleaf 是新一代的模板引擎,在spring4.0中推薦使用thymeleaf來做前端模版引擎。
    2017-05-05

最新評論

边坝县| 西平县| 合水县| 郸城县| 云南省| 宁蒗| 滦平县| 那坡县| 炉霍县| 福州市| 宜章县| 汨罗市| 雷州市| 天长市| 竹北市| 鄯善县| 辽宁省| 长宁区| 长沙县| 华坪县| 永年县| 广东省| 图片| 辽源市| 蓬安县| 鄂伦春自治旗| 永善县| 沾化县| 揭西县| 离岛区| 金川县| 台江县| 鄂温| 潮安县| 舒兰市| 和顺县| 西丰县| 宜良县| 砀山县| 乐亭县| 谢通门县|