日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)java → C#之int與Java之Integer的區(qū)別

      C#之int與Java之Integer的區(qū)別

      相關(guān)軟件相關(guān)文章發(fā)表評論 來源:本站整理時間:2010/10/24 14:00:09字體大。A-A+

      作者:佚名點擊:230次評論:0次標簽: C# Java

      • 類型:遠程監(jiān)控大小:4.6M語言:中文 評分:5.7
      • 標簽:
      立即下載

      本文涉及到一些JVM原理和Java的字節(jié)碼指令,推薦感興趣的讀者閱讀一本有關(guān)JVM的經(jīng)典書籍《深入Java虛擬機(第2版)》,將它與我在《.NET 4.0面向?qū)ο缶幊搪劇分薪榻B的CLR原理與IL匯編指令作個對比,相信讀者會有一定的啟發(fā)。而仔細對比兩個類似事物的異同,是很有效的學習方法之一。

      1 奇特的程序輸出

                前段時間,一個學生給我看了一段“非常詭異”的Java代碼:
         public class TestInteger {
          public static void main(String[] args){
              Integer v1=100;
              Integer v2=100;
              System.out.println(v1==v2); //輸出:true
              Integer w1=200;
              Integer w2=200;
              System.out.println(w1==w2); //輸出:false
          }
      }

                讓這個學生最困惑的是,為什么這些如此相似的代碼會有這樣令人意外的輸出?

               我平時多使用C#,Java用得不多,初看到這段代碼的輸出,我也同樣非常奇怪:怎么會這樣呢?100和200這兩個整型數(shù)值對Integer這個類有本質(zhì)上的差別嗎?

               為了弄明白出現(xiàn)上述現(xiàn)象的底層原因,我使用javap工具反匯編了Java編譯器生成的.class文件:

         

                通過仔細閱讀Java編譯器生的字節(jié)碼,我發(fā)現(xiàn)以下給Integer變量賦值的語句:

               Integer v1=100;

                實際上調(diào)用的是Integer.valueOf方法。

                 而完成兩個Integer變量比較的以下語句:

                System.Console.WriteLine(v1 == v2);

                實際生成的是if_acmpne指令。其中的a代表“address”,cmp代表“Compare”,ne代表“not equal”。

               這條指令的含義是:比較Java方法棧中的兩個操作數(shù)(即v1與v2),看看它們是不是指向堆中的同一個對象。

               當給v1和v2賦值100時,它們將引用同一個Integer對象。

               那為什么當值改為200時,w1和w2就“翻臉了”,分別引用不同的Integer對象?

               秘密就在于Integer.valueOf方法。幸運的是,Java的類庫是開源的,所以我們可以毫不費力地看到相關(guān)的源代碼:

       public static Integer valueOf(int i) {
              if(i >= -128 && i <= IntegerCache.high)
                  return IntegerCache.cache[i + 128];
              else
                  return new Integer(i);
          }

              一切真相大白,原來Integer在內(nèi)部使用了一個私有的靜態(tài)類IntegerCache,此類內(nèi)部封裝了一個Integer對象的cache數(shù)組來緩存Integer對象,其代碼如下:

       private static class IntegerCache {
            static final Integer cache[];
            //……
      }

               再仔細看看IntegerCache內(nèi)部的代碼,會看到它使用靜態(tài)初始化塊在cache數(shù)組中保存了[-128,127]區(qū)間內(nèi)的一共256個Integer對象。

               當給Integer變量直接賦整數(shù)值時,如果這個數(shù)值位于[-128,127]內(nèi),JVM(Java Virtual Machine)就直接使用cache中緩存的Integer對象,否則,JVM會重新創(chuàng)建一個Integer對象。

               一切真相大白。

       2 進一步探索Integer

           我們再進一步地看看這個有趣的Integer:

           Integer v1 = 500;
          Integer v2 = 300;
          Integer addResult = v1 + v2;   //結(jié)果:800
          double divResult = (double)v1/v2;   //結(jié)果:1.6666666666666667

               喲,居然Integer對象支持加減乘除運算耶!它是怎么做到的?

               再次使用javap反匯編.class文件,不難發(fā)現(xiàn):

              Integer類的內(nèi)部有一個私有int類型的字段value,它代表了Integer對象所“封裝”的整數(shù)值。

                private final int value;

                當需要執(zhí)行v1+v2時,JVM會調(diào)用v1和v2兩個Integer對象的intValue方法取出其內(nèi)部所封裝的整數(shù)值value,然后調(diào)用JVM直接支持的iadd指令將這兩個整數(shù)直接相加,結(jié)果送回方法棧中,然后調(diào)用Integer.valueOf方法轉(zhuǎn)換為Integer對象,讓addResult變量引用這一對象。

               除法則復雜一點,JVM先調(diào)用i2d指令將int轉(zhuǎn)換為double,然后再調(diào)用ddiv指令完成浮點數(shù)相除的工作。

               通過上述分析,我們可以知道,其實Integer類本身并不支持加減乘除,而是由Java編譯器將這些加減乘除的語句轉(zhuǎn)換為JVM可以直接執(zhí)行的字節(jié)碼指令(比如本例中用到的iadd和ddiv),其中會添加許多條用于類型轉(zhuǎn)換的語句。

               由此可見,與原始數(shù)據(jù)類型int相比,使用Integer對象直接進行加減乘除會帶來較低的運行性能,應(yīng)避免使用。

       3 JDK中Integer類的“彎彎繞”設(shè)計方案

                現(xiàn)在,我們站在一個更高的角度,探討一下Integer的設(shè)計。

               我個人認為,給Integer類型添加一個“對象緩沖”不是一個好的設(shè)計,從最前面的示例代碼大家一定會感到這一設(shè)計給應(yīng)用層的代碼帶來了一定的混亂。另外,我們看到JDK設(shè)計者只緩存了[-128,127]共256個Integer對象,他可能認為這個區(qū)間內(nèi)的整數(shù)是最常用的,所以應(yīng)該緩存以提升性能。就我來看,這未免有點過于“自以為是”了,說這個區(qū)間內(nèi)的Integer對象用得最多有什么依據(jù)?對于那些經(jīng)常處理>128的整數(shù)值的應(yīng)用程序而言,這個緩存一點用處也沒有,是個累贅。就算真要緩存,那也最好由應(yīng)用程序開發(fā)者自己來實現(xiàn),因為他可以依據(jù)自己開發(fā)的實際情況緩存真正用到的對象,而不需背著這個包容著256個Integer對象的大包袱。

               而且前面也看到了,基于Integer對象的加減乘除會增加許多不必要的類型轉(zhuǎn)換指令,遠不如直接使用原始數(shù)據(jù)類型更快捷更可靠。

               其實上用得最多的不是Integer對象而是它所封裝的一堆靜態(tài)方法(這些方法提供了諸如類型轉(zhuǎn)換等常用功能),我很懷疑在實際開發(fā)中有多少場合需要去創(chuàng)建大量的Integer對象,而且還假設(shè)它們封裝的數(shù)值還位于[-128,127]區(qū)間之內(nèi)?

             緩存Integer對象還對多線程應(yīng)用程序帶來了一定的風險,因為可能會有多個線程同時存取同一個緩存了的Integer對象。不過JDK設(shè)計者已經(jīng)考慮到了這個問題,我看到Integer類的字段都是final的,不可改,是一個不可變類,所以可以在多線程環(huán)境下安全地訪問。盡管在使用上沒問題,但這一切是不是有點彎彎繞?去掉這個對象緩存,Integer類型是不是“更輕爽”“更好用”?

       4  C# int挑戰(zhàn)Java Integer

                將Java的設(shè)計與.NET(以C#為例)的設(shè)計作個比較是有趣的。

               Java將數(shù)據(jù)類型分為“原始數(shù)據(jù)類型”和“引用數(shù)據(jù)類型”兩大類,int是原始數(shù)據(jù)類型,為了向開發(fā)者提供一些常用的功能(比如將String轉(zhuǎn)換為int),所以JDK提供了一個引用類型Integer,封裝這些功能。

               .NET則不一樣,它的數(shù)據(jù)類型分為“值類型”和“引用數(shù)據(jù)類型”兩大類,int屬于值類型,本身就擁有豐富的方法,請看以下C#代碼:

           int i = 100;
          string str = i.ToString(); //int變量本身就擁有“一堆”的方法

                使用.NET的反匯編器ildasm查看一下上述代碼生成的IL指令,不難發(fā)現(xiàn)C#編譯器會將int類型映射為System.Int32結(jié)構(gòu):

        

               注意System.Int32是一個值類型,生存于線程堆棧中,一般來說,在多線程環(huán)境下,使用值類型的變量往往比引用類型的變量更安全,因為它減少了多線程訪問同一對象所帶來的問題。

       =================================

               簡要解釋一下:請對比以下兩個方法:

                void DoSomethingWithValueType(int value);

                void DoSomethingWithReferenceType(MyClass obj);

               當多個線程同時執(zhí)行上述兩個方法時,線程函數(shù)使用值類型的參數(shù)value是比較安全的,不用擔心多個線程互相影響,但引用類型的obj參數(shù)就要小心了,如果多個線程接收到的obj參數(shù)有可能引用同一個MyClass對象,為保證運行結(jié)果的正確,有可能需要給此對象加鎖。

      ====================================       

               與JVM一樣,.NET的CLR也提供了add等專用指令完成加減乘除功能。

               從開發(fā)者使用角度而言,C#的int既具有與Java的原始數(shù)據(jù)類型int一樣的在虛擬機級別的專用指令,又具有Java包裝類Integer所擁有的一些功能,還同時避免了Java中Integer的那種比較古怪的特性,個人認為,C#中的int比Java中的int/Integer更好用,更易用。

               但從探索技術(shù)內(nèi)幕而言則大不一樣,Java使用Integer一個類就“搞定”了所有常用的整數(shù)處理功能,而對于.NET的System.In32結(jié)構(gòu),好奇的朋友不妨用Reflector去查看一下相關(guān)的源碼,會發(fā)現(xiàn)System.Int32在內(nèi)部許多地方使用了Number類所封裝的功能,還用到了NumberFormatInfo(提供數(shù)字的格式化信息)、CultureInfo(提供當前文化信息)等相關(guān)類型,如果再算加上一堆的接口,那真是“相當?shù)亍睆碗s。

               比對一下Java平臺與.NET平臺,往往會發(fā)現(xiàn)在許多地方Java封裝得較少。

               從應(yīng)用程序開發(fā)角度來看,不少地方Java在使用上不如.NET方便。就拿本文所涉及的非常常見的整數(shù)類型及其運算而言,相信大家都看到了,使用Java編程需要留心這個“Intege對象緩存”的陷阱,而.NET則很貼心地把這些已發(fā)現(xiàn)的陷阱(.NET設(shè)計者說:當然肯定會有沒發(fā)現(xiàn)的陷阱,但那就不關(guān)我事了)都蓋上了“厚厚”的井蓋,讓開發(fā)者很省心,因而帶來了較高的開發(fā)效率和較好的開發(fā)體驗。

               但另一方面,Java的JDK代碼一覽無余,是開放的,你要探索其技術(shù)內(nèi)幕,總是很方便,這點還是比較讓人放心。

              .NET則相對比較封閉,總是遮遮掩掩,想一覽其廬山真相還真不容易,而且我感覺它為開發(fā)者考慮得太周到了,服務(wù)得太好了,這不見得是一件好事。因為人性的弱點之一就是“好逸惡勞”,生活太舒服了,進取精神就會少掉不少,.NET開發(fā)者很容易于不知不覺中養(yǎng)成了對技術(shù)不求甚解的“惡習”,因為既然代碼能夠正常工作,那又何必費心地去追根問底?但話又說回來,如果僅滿足于知其然,又怎會在技術(shù)上有所進步和提高?等到年紀一大,就被年輕人給淘汰了。而這種現(xiàn)象的出現(xiàn),到底應(yīng)該怪微軟,怪周遭的環(huán)境,還是自己呢?

        相關(guān)評論

        閱讀本文后您有什么感想? 已有人給出評價!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評論

        最新評論

        發(fā)表評論 查看所有評論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字數(shù): 0/500 (您的評論需要經(jīng)過審核才能顯示)