<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 241,  comments - 116,  trackbacks - 0

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

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

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

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

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

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

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

    1 奇特的程序輸出

             前段時間,一個學(xué)生給我看了一段“非常詭異”的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

        }

    }

     

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

     

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

     

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

     

     

          

        通過仔細(xì)閱讀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ù)(即v1v2),看看它們是不是指向堆中的同一個對象。

     

             當(dāng)給v1v2賦值100時,它們將引用同一個Integer對象。

     

     

             那為什么當(dāng)值改為200時,v1v2就“翻臉了”,分別引用不同的Integer對象?

     

             秘密就在于Integer.valueOf方法。幸運(yùn)的是,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[];

                    //……

             }

     

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

     

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

     

             一切真相大白。

     

    2 進(jìn)一步探索Integer

     

        我們再進(jìn)一步地看看這個有趣的Integer

     

             Integer v1=500;

             Integer v2=300;

             Integer addResult=v1+v2;  //結(jié)果:800

             double divResult=(double)v1/v2;      //結(jié)果:1.6666666666666667

     

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

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

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

     

             private final int value;

     

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

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

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

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

     

    3 JDKInteger類的“彎彎繞”設(shè)計方案

     

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

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

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

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

           緩存Integer對象還對多線程應(yīng)用程序帶來了一定的風(fē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);

     

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

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

            

    posted on 2010-10-27 16:39 墻頭草 閱讀(319) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    人人游戲網(wǎng) 軟件開發(fā)網(wǎng) 貨運(yùn)專家
    主站蜘蛛池模板: 免费国产a国产片高清网站| 国产成人亚洲综合无码| 女bbbbxxxx另类亚洲| 亚洲精品自产拍在线观看| 亚洲国产成人久久综合碰| 免费毛片在线看不用播放器| 亚洲美女aⅴ久久久91| 全免费一级午夜毛片| 精品视频在线免费观看| 亚洲精品无码专区在线播放| 亚洲日韩v无码中文字幕| 日韩吃奶摸下AA片免费观看| 国产免费人成视频尤勿视频| 亚洲va在线va天堂va手机| 4338×亚洲全国最大色成网站| 免费观看激色视频网站bd| 一级黄色免费毛片| 亚洲乱码在线观看| 国产亚洲精品一品区99热| 国产麻豆免费观看91| 91精品导航在线网址免费| 草久免费在线观看网站| 亚洲AV综合色区无码二区爱AV| 亚洲日韩欧洲乱码AV夜夜摸| 男女啪啪永久免费观看网站| 最近中文字幕完整免费视频ww | 国产精品久久久久久久久久免费| 无套内谢孕妇毛片免费看看| 33333在线亚洲| 久久久久亚洲精品美女| 亚洲精品黄色视频在线观看免费资源| 妻子5免费完整高清电视| 亚洲理论精品午夜电影| 最近中文字幕mv免费高清视频7 | 国产综合成人亚洲区| 亚洲片一区二区三区| 成人毛片18女人毛片免费96| 亚洲黄色免费网站| 中文字幕av免费专区| 日本一区二区三区在线视频观看免费| 亚洲精品伊人久久久久|