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

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

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

    隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
    數(shù)據(jù)加載中……

    關于Java String對象創(chuàng)建問題解惑

    本文為原創(chuàng),如需轉(zhuǎn)載,請注明作者和出處,謝謝!

    先看看下面的代碼

        
    public String makinStrings()
        {
            String s 
    = "Fred";
            s 
    = s + "47";
            s 
    = s.substring(25);
            s 
    = s.toUpperCase();
            
    return s.toString();
        }


    問:調(diào)用makinStrings方法會創(chuàng)建幾個String對象呢。  答案:3個


        上面的方法有五條語句:現(xiàn)在讓我們來一條一條分析一下。

    String s = "Fred";   結論:創(chuàng)建了一個String對象

    這條語句相當于String s = new String("Fred");
    因此,毫無疑問,第一條語句創(chuàng)建了一個String對象,我想沒有有疑問吧?

    s = s + "47";   結論:未創(chuàng)建String對象

    這條語句也許很多人認為是創(chuàng)建了String對象,我一開始也是這么認為的。但是為了驗證我的想法。決定
    用點法術恢復這條語句的本來面目。(有很多時候,編譯器總是在里面搞一些小動作,javac.exe也不例外)

    現(xiàn)在找到這個程序所生成的.class文件(假設是Test.class),找一個反編譯工具,我推薦JAD,可以http://www.softpedia.com/progDownload/JAD-Download-85911.html下載
    下載后,有一個jad.exe,將其路徑放到環(huán)境變量path中(只限windows)。并在.class文件的當前路徑執(zhí)行如下的命令:

    jad Test

    然后大喊一聲“還我本來面目”

    會在當前目錄下生成一個Test.jad文件,打開它,文件內(nèi)容如下:

     
        
    public String makinStrings()
        {
            String s 
    = "Fred";
            s 
    = (new StringBuilder(String.valueOf(s))).append("47").toString();
            s 
    = s.substring(25);
            s 
    = s.toUpperCase();
            
    return s.toString();
        }
     

        哈哈,其他的語句都沒變,只有第二條變長了,雖然多了個new,但是建立的是StringBuilder對象。原來
    這是java編譯器的優(yōu)化處理。原則是能不建String對象就不建String對象。而是用StringBuilder對象
    加這些字符串連接起來,相當于一個字符串隊列。這種方式尤其被使用在循環(huán)中,大家可以看看下面的代碼:
            String s = "";
            for(int i=0; i < 10000000; i++)
                s += "aa";
        沒有哪位老大認為這是建立了10000000個String對象吧。但不幸的是,上面的代碼雖然沒有建立10000000個String對象
    但卻建立了10000000個StringBuilder對象,那是為什么呢,自已用jad工具分析一下吧。
    正確的寫法應該是:

            StringBuilder sb = new StringBuilder("");
            for(int i=0; i < 10000000; i++)
                sb.append(String.valueOf(i));

     s = s.substring(2, 5);     結論:創(chuàng)建了一個String對象
     也許有很多人一開始就認為這條語句是創(chuàng)建了一個String對象,那么恭喜你,這條語句確實創(chuàng)建了一個String對象
     實際上就是substring方法創(chuàng)建了一個String對象。這也沒什么復雜的,自已下一個JDK源代碼,看看substring是如何實現(xiàn)的
     就可以知道了。我先說一下吧。先不用管substring是如何實現(xiàn)的,反正在substring方法返回時使用了一個new顯式地建立了一個String對象
     不信自己看看源碼。
    s = s.toUpperCase();   結論:創(chuàng)建了一個String對象

    toUpperCase()和substring方法類似,在返回時也是使用了new建立了一個String對象。

    return s.toString();   結論:未創(chuàng)建String對象

    toString方法返回的就是this,因此,它的返回值就是s。

    這道題還算比較簡單,再給大家出一個更復雜一點的,也是關于String對象的創(chuàng)建的(只是改了一個原題)。

        public String makinStrings()
        {
            String s 
    = "Fred";
            s 
    = s + "Iloveyou.".substring(1).toLowerCase();
            s 
    = s.substring(0);
            s 
    = s.substring(0,1).toUpperCase();
            
    return s.toString();
        }


    先公布答案吧,上述代碼也創(chuàng)建了3個String對象,哈哈!


    為什么呢?

    要想知道為什么,先得弄清楚substring、toLowerCase和toUpperCase什么時候創(chuàng)建String對象,什么時候不創(chuàng)建對象。

    substring方法在截取的子字符串長度等于原字符串時,直接返回原字符串。并不創(chuàng)建新的String對象。

    toLowerCase方法在字符串中更本沒有需要轉(zhuǎn)換的大寫字母時直接返回原字符串,如"abcd".toLowerCase()直接返回abcd,并不創(chuàng)建新的String對象

    toUpperCase方法和toLowerCase類似。"ABCD".toUpperCase()直接返回ABCD。


    知道了這個,上面的代碼就非常清楚了。

        public String makinStrings()
        {
            String s 
    = "Fred";     // 創(chuàng)建一個String對象
            s = s + "Iloveyou.".substring(1).toLowerCase();  // substring(1)創(chuàng)建一個String對象,由于toLowerCase()轉(zhuǎn)換的字符串是"loveyou.",沒有大寫字母,因此,它不創(chuàng)建新的String對象
            s = s.substring(0);   // 由于substring(0)截獲的是s本身,因此,這條語句不創(chuàng)建新的String對象
            s = s.substring(0,1).toUpperCase();  // substring(0,1)創(chuàng)建了一個String對象,但由于substring(0,1)的結果是"F",為一個大寫字母,因此,toUpperCase直接返回"F"本身。
            return s.toString();
        }






    Android開發(fā)完全講義(第2版)(本書版權已輸出到臺灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2008-04-27 10:01 銀河使者 閱讀(3168) 評論(18)  編輯  收藏 所屬分類: java 原創(chuàng)

    評論

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    既然考慮編譯器因素了,為什么不考慮編譯器直接把這段代碼constant folding + constant propagation然后直接返回一個最終結果呢?
    2008-04-27 10:33 | ZelluX

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    好文!!!
    2008-04-27 10:33 | tomjamescn

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    如果之前有個函數(shù),里面已經(jīng)執(zhí)行了過
    String s = "Fred";
    那么然后你再調(diào)用 makinStrings() 的時候,第一步應該不再創(chuàng)建一個 "Fred"了吧?
    2008-04-27 10:46 | stanleyxu

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    你是自己研究的吧,基本上差不多,只是
    String s = "Fred";
    這句不會在運行時創(chuàng)建String對象。
    s = (new StringBuilder(String.valueOf(s))).append("47").toString();
    這句StringBuilder.toString();是會創(chuàng)建String對象的。

    這樣一加一減,你的答案還是可用的。
    2008-04-27 11:01 | Matthew Chen

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    to Matthew Chen

    String s = "Fred"; 這句我查了一下bytecode,應該是一個壓棧的操作。如果要是創(chuàng)建String對象,就是四個String對象了。

    StringBuilder類的toString確實創(chuàng)建了一個String對象,下面是toString方法的代碼。

    public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
    }

    2008-04-27 11:15 | 銀河使者

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    java應該和delphi和vc一樣,使用了引用計數(shù)原理。
    所以以此類推,給字符串賦值常量的時候,該常量是編譯時由編譯器(在堆上)創(chuàng)建。
    所以String s = "Fred";嚴格的說,第一次使用的時候是為它創(chuàng)建了一個實例。
    其他的部分你自己也已經(jīng)說明了:
    如果小寫字符串執(zhí)行toLowerCase()不會再(在棧上)創(chuàng)建實例,而是對它的引用計數(shù)加一。
    如果執(zhí)行substring(0),同理。
    賦值的話,難說!如果有另外一個string b=s;然后你再執(zhí)行s+='new..';可能就是會再創(chuàng)建一個實例,而不是不變了。(至少delphi里面是這樣的)

    麻煩lz驗證一下我的話,我只是從語義出發(fā),做的一些推斷。
    2008-04-27 11:34 | stanleyxu

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    沒有必要太在意這個問題.
    因為不同的jdk下,比如sun jdk,ibm jdk等,答案是非唯一的.而且不同的jdk版本下,也有可能存在不同的答案.
    sun jdk是對String優(yōu)化做的最多的.所以才這么拗.很多公司都拿string做面試題,沒有意義的.
    至于s=s+"47",至少在jdk5以下,在String pool中肯定會創(chuàng)建一個新的對象.但是之后的版本怎么處理,就沒去看過了.
    2008-04-27 12:04 | stone2083

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    補充一點:lz未說明從計算每步是否創(chuàng)建string實例還是問從函數(shù)返回時,內(nèi)存中還有多少實例?這個結果是不一樣的。根據(jù)引用計數(shù)的原理,當一個字符串的refcount為0之后,會被釋放。而且根據(jù)編譯器的不同,可能會采取不同的策略。如果字符串變長,應該會去開辟一塊新的內(nèi)存,而如果字符串變短,可能只是在原有的內(nèi)存上做少許處理。
    2008-04-27 12:09 | stanleyxu

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    再補充一點:樓上的stone2083提醒了我,字符串變長的時候,應該要創(chuàng)建一個新的變量。我不太清楚string在java中如何分配的,一般應該是array形式,即申請一塊連續(xù)的內(nèi)存。這樣的話,如果要加長字符串,應該要重新申請一塊新的連續(xù)內(nèi)存了。因為編譯器無法保證當前字符串所占內(nèi)存的后續(xù)連續(xù)地址是否是可用的。
    2008-04-27 12:12 | stanleyxu

    # re: 關于Java String對象創(chuàng)建問題解惑[未登錄]  回復  更多評論   

    to 銀河使者
    看不懂你的意思,是認同我的說法嗎?因為看你的回復和我的意思基本一致。

    to stanleyxu
    你提到了編譯器,我想那時生成的保存在靜態(tài)對象池中的string是字節(jié)碼的一部分,運行時調(diào)入內(nèi)存的,而在內(nèi)存中直接創(chuàng)建的string對象確實和普通的數(shù)組一樣的方式,沒有為連接做特別的結構考慮。
    2008-04-27 22:05 | Matthew Chen

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    據(jù)說這是一道SCJP的考試題,哈哈,哪天再弄點SCJP的試題分析一下。再來幾次頭腦風暴!
    2008-04-27 22:21 | 銀河使者

    # re: 關于Java String對象創(chuàng)建問題解惑[未登錄]  回復  更多評論   

    String s = "Fred"; 結論:創(chuàng)建了一個String對象

    這條語句相當于String s = new String("Fred");
    因此,毫無疑問,第一條語句創(chuàng)建了一個String對象,我想沒有有疑問吧?

    最近在聽浪曦網(wǎng)風中葉老師的面試視頻,聽他講 String s = new String("Fred");這句話應該是創(chuàng)建了2個對象,那你說String s = "Fred";這條語句相當于String s = new String("Fred");行嗎?到底誰是對的?
    2008-04-28 17:26 | 在威尼斯流浪

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    String s = new String("Fred")這句,new 肯定是創(chuàng)建了一個String對象。但是"Fred",我看了一下bytecode,好象是直接壓棧了,并不創(chuàng)建String對象。
    2008-04-28 19:29 | 銀河使者

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    下面是String s = new String("Fred")的bytecode,看看吧,只有一個new

    public class Test
    {
    public Test()
    {
    // 0 0:aload_0
    // 1 1:invokespecial #1 <Method void Object()>
    // 2 4:return
    }

    public void main(String args[])
    {
    String s = new String("Fred");
    // 0 0:new #2 <Class String>
    // 1 3:dup
    // 2 4:ldc1 #3 <String "Fred">
    // 3 6:invokespecial #4 <Method void String(String)>
    // 4 9:astore_2
    // 5 10:return
    }
    }

    還有就是在本文中我的分析有一點小毛病,就是String s = "Fred"的確在運行時不創(chuàng)建String對象,只是壓棧操作,另外說String s = "Fred"相當于String s = new String("Fred")也不嚴謹。從底層上它們還是有區(qū)別的。但可以肯定的是String s = new String("Fred")在當前方法中肯定是建立一個String對象,而String s = "Fred"并不創(chuàng)建對象。而StringBuilder的toString創(chuàng)建了一個String對象。這從上面的bytecode就可以看出。

    四樓的Matthew Chen說的沒錯。多謝提醒。
    2008-04-28 19:42 | 銀河使者

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    從評論看出lz也沒真正明白字符串分配究竟在干什么。

    String s1 = "Fred";
    String s2 = "Fred";
    // s1 == s2

    String s1 = new String("Fred");
    String s2 = "Fred";
    // s1 != s2

    String s1 = new String("Fred");
    String s2 = new String("Fred");
    // s1 != s2

    看看我之前的評論然后自己慢慢吧。
    2008-04-29 12:41 | stanleyxu

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    to tanleyxu

    從bytecode上看是String s = "abc" 不建立String對象。其實也沒必要這么較真。至于jvm內(nèi)部到底如何工作,只有看jvm的源代碼了。jvm、MSIL這些東西對String對處理都不太一樣,無法只從語意上分析。不知道tanleyxu最后這個評論:

    String s1 = "Fred";
    String s2 = "Fred";
    // s1 == s2

    String s1 = new String("Fred");
    String s2 = "Fred";
    // s1 != s2

    String s1 = new String("Fred");
    String s2 = new String("Fred");
    // s1 != s2

    是什么意思.如果String s = "Fred", s是壓棧操作,用的是方法棧中的空間。new String(),用的是堆的空間,這沒錯。難道我最后補充的有問題。我可不這么認為。實際上String s = "aa"確實不創(chuàng)建新的String。反正在bytecode里沒找到new。



    2008-04-29 13:15 | 銀河使者

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    傻逼文章
    2008-06-06 10:04 | w

    # re: 關于Java String對象創(chuàng)建問題解惑  回復  更多評論   

    哈哈,還是得看評論啊。熱鬧。
    2015-05-28 17:30 | 莾s
    主站蜘蛛池模板: 久久精品九九亚洲精品| 国产真人无码作爱免费视频| 亚洲色精品aⅴ一区区三区| 丁香花免费完整高清观看| 黄色视频在线免费观看| 亚洲乱码国产乱码精华| 亚洲麻豆精品果冻传媒| 亚洲精品无码永久中文字幕| 国产乱子影视频上线免费观看| 免费毛片a在线观看67194| 日韩免费高清大片在线| 中文字幕手机在线免费看电影| 美女无遮挡免费视频网站| 亚洲国产精品嫩草影院| 亚洲国产高清美女在线观看| 亚洲电影一区二区三区| 国产亚洲综合网曝门系列| 亚洲国产小视频精品久久久三级| 在线免费观看一级毛片| 国产福利在线免费| 黄在线观看www免费看| 最近2019免费中文字幕视频三| 在线观看免费黄网站| 亚洲黄片手机免费观看| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 久久精品成人免费观看| 国产免费AV片在线观看播放| 美女免费精品高清毛片在线视| 亚洲中文字幕一二三四区| 亚洲国产综合在线| 亚洲国产高清在线精品一区| 亚洲国产成人精品无码区在线网站| 国产亚洲精品一品区99热| 国产成人亚洲综合无码精品| 中文字幕亚洲第一| 国产性爱在线观看亚洲黄色一级片 | 女人18毛片水真多免费看| 好吊妞998视频免费观看在线| 午夜免费福利在线| 黄网址在线永久免费观看| 国产真实伦在线视频免费观看|