中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

深入(ru)理(li)解(jie)Java:String

在講解String之前,我們先了解一下Java的內存結構。

 

一、Java內存模型

 

按照官方的(de)說法:Java 虛擬機具有一個堆,堆是運行時數(shu)據區域,所有類實例和(he)數(shu)組的(de)內存均從此(ci)處(chu)分配。

    JVM主要(yao)管理兩種類型內存:堆(dui)和非堆(dui),堆(dui)內存(Heap Memory)是(shi)在 Java 虛擬機啟(qi)動時創建,非堆(dui)內存(Non-heap Memory)是(shi)在JVM堆(dui)之(zhi)外的內存。

簡單來說,非(fei)堆包含方(fang)法(fa)(fa)區、JVM內(nei)部處理或(huo)優化所需的內(nei)存(如 Compiler,Just-in-time Compiler,即時(shi)編譯后的代碼緩存)、每個類結(jie)構(如運行時(shi)常數(shu)(shu)池、字段(duan)和(he)方(fang)法(fa)(fa)數(shu)(shu)據)以及方(fang)法(fa)(fa)和(he)構造方(fang)法(fa)(fa)的代碼。

    Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、 anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由于要在運行時動態分配內存,存取速度較慢。 

  棧的優勢是,存取速度比堆要快,僅次于寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變量數據(int, short, long, byte, float, double, boolean, char)和對象句柄(引用)。 

    虛(xu)擬機必須為(wei)每個被裝載的類型維護一(yi)個常(chang)量池(chi)。常(chang)量池(chi)就是該類型所用(yong)到常(chang)量的一(yi)個有序集合,包括直接常(chang)量(string,integer和 floating point常(chang)量)和對其他(ta)類型,字段和方法的符號引用(yong)。 

  對(dui)于String常(chang)(chang)量(liang),它的(de)(de)值(zhi)是在常(chang)(chang)量(liang)池中的(de)(de)。而(er)JVM中的(de)(de)常(chang)(chang)量(liang)池在內存(cun)當(dang)中是以表(biao)(biao)的(de)(de)形式存(cun)在的(de)(de), 對(dui)于String類(lei)型(xing),有(you)(you)一(yi)(yi)張固(gu)定(ding)長度的(de)(de)CONSTANT_String_info表(biao)(biao)用(yong)(yong)來存(cun)儲(chu)文字(zi)(zi)字(zi)(zi)符(fu)串值(zhi),注意:該表(biao)(biao)只(zhi)存(cun)儲(chu)文字(zi)(zi)字(zi)(zi)符(fu)串值(zhi),不存(cun)儲(chu)符(fu)號引用(yong)(yong)。說到(dao)這里,對(dui)常(chang)(chang)量(liang)池中的(de)(de)字(zi)(zi)符(fu)串值(zhi)的(de)(de)存(cun)儲(chu)位置應該有(you)(you)一(yi)(yi)個比較明了的(de)(de)理解了。在程序執行的(de)(de)時(shi)候,常(chang)(chang)量(liang)池會儲(chu)存(cun)在Method Area,而(er)不是堆中。常(chang)(chang)量(liang)池中保存(cun)著很多String對(dui)象; 并且可(ke)以被共享使(shi)用(yong)(yong),因此它提高(gao)了效率

 具體(ti)關于JVM和(he)內(nei)存等知識(shi)請參考:

二、案例解析

 

public static void main(String[] args) {  
        /** 
         * 情景一:字(zi)符串(chuan)池(chi) 
         * JAVA虛(xu)擬機(JVM)中(zhong)存在著(zhu)一個字(zi)符串(chuan)池(chi),其中(zhong)保(bao)存著(zhu)很多String對象; 
         * 并且(qie)可(ke)以(yi)被共享使(shi)用,因此它提高(gao)了效率(lv)。 
         * 由(you)于String類(lei)是final的,它的值一經創建就不可(ke)改變。 
         * 字(zi)符串(chuan)池(chi)由(you)String類(lei)維(wei)護(hu),我們可(ke)以(yi)調(diao)用intern()方法(fa)來訪問字(zi)符串(chuan)池(chi)。  
         */  
        String s1 = "abc";     
        //↑ 在字符(fu)串池(chi)創建(jian)了一個對象  
        String s2 = "abc";     
        //↑ 字符串(chuan)pool已(yi)經存在(zai)對(dui)象(xiang)(xiang)“abc”(共享),所以創建0個對(dui)象(xiang)(xiang),累計創建一個對(dui)象(xiang)(xiang)  
        System.out.println("s1 == s2 : "+(s1==s2));    
        //↑ true 指向同一個對(dui)象,  
        System.out.println("s1.equals(s2) : " + (s1.equals(s2)));    
        //↑ true  值(zhi)相等(deng)  
        //↑------------------------------------------------------over  
        /** 
         * 情景(jing)二:關于new String("") 
         *  
         */  
        String s3 = new String("abc");  
        //↑ 創建了(le)兩(liang)個對象,一(yi)個存放在字符串池(chi)中(zhong),一(yi)個存在與堆(dui)區中(zhong);  
        //↑ 還有一個對(dui)象引(yin)用s3存放在棧中(zhong)  
        String s4 = new String("abc");  
        //↑ 字符串池中已經存(cun)在“abc”對象,所(suo)以只在堆中創(chuang)建(jian)了一個對象  
        System.out.println("s3 == s4 : "+(s3==s4));  
        //↑false   s3和s4棧(zhan)區的(de)地址(zhi)不(bu)同,指向堆區的(de)不(bu)同地址(zhi);  
        System.out.println("s3.equals(s4) : "+(s3.equals(s4)));  
        //↑true  s3和s4的值(zhi)相(xiang)同  
        System.out.println("s1 == s3 : "+(s1==s3));  
        //↑false 存放的地區(qu)多不同,一個棧(zhan)區(qu),一個堆區(qu)  
        System.out.println("s1.equals(s3) : "+(s1.equals(s3)));  
        //↑true  值(zhi)相同  
        //↑------------------------------------------------------over  
        /** 
         * 情景三(san):  
         * 由于(yu)常(chang)量的值(zhi)在編譯的時(shi)(shi)候就(jiu)被確(que)定(優化)了(le)。 
         * 在這里,"ab"和(he)"cd"都是常(chang)量,因(yin)此變量str3的值(zhi)在編譯時(shi)(shi)就(jiu)可(ke)以確(que)定。 
         * 這行代碼(ma)編譯后的效果等同于(yu): String str3 = "abcd"; 
         */  
        String str1 = "ab" + "cd";  //1個對象(xiang)  
        String str11 = "abcd";   
        System.out.println("str1 = str11 : "+ (str1 == str11));  
        //↑------------------------------------------------------over  
        /** 
         * 情景四(si):  
         * 局(ju)部變量(liang)str2,str3存(cun)儲的(de)(de)(de)(de)是存(cun)儲兩個拘(ju)(ju)留(liu)字(zi)(zi)(zi)符串(chuan)對(dui)(dui)(dui)(dui)(dui)象(intern字(zi)(zi)(zi)符串(chuan)對(dui)(dui)(dui)(dui)(dui)象)的(de)(de)(de)(de)地址(zhi)。 
         *  
         * 第三行代碼原理(li)(str2+str3): 
         * 運行期JVM首先(xian)會在堆(dui)中(zhong)(zhong)(zhong)創(chuang)建(jian)一個StringBuilder類(lei), 
         * 同(tong)時用str2指(zhi)向的(de)(de)(de)(de)拘(ju)(ju)留(liu)字(zi)(zi)(zi)符串(chuan)對(dui)(dui)(dui)(dui)(dui)象完(wan)成初始化(hua), 
         * 然后調用append方(fang)法完(wan)成對(dui)(dui)(dui)(dui)(dui)str3所指(zhi)向的(de)(de)(de)(de)拘(ju)(ju)留(liu)字(zi)(zi)(zi)符串(chuan)的(de)(de)(de)(de)合并, 
         * 接著調用StringBuilder的(de)(de)(de)(de)toString()方(fang)法在堆(dui)中(zhong)(zhong)(zhong)創(chuang)建(jian)一個String對(dui)(dui)(dui)(dui)(dui)象, 
         * 最后將剛(gang)生成的(de)(de)(de)(de)String對(dui)(dui)(dui)(dui)(dui)象的(de)(de)(de)(de)堆(dui)地址(zhi)存(cun)放在局(ju)部變量(liang)str3中(zhong)(zhong)(zhong)。 
         *  
         * 而str5存(cun)儲的(de)(de)(de)(de)是字(zi)(zi)(zi)符串(chuan)池中(zhong)(zhong)(zhong)"abcd"所對(dui)(dui)(dui)(dui)(dui)應的(de)(de)(de)(de)拘(ju)(ju)留(liu)字(zi)(zi)(zi)符串(chuan)對(dui)(dui)(dui)(dui)(dui)象的(de)(de)(de)(de)地址(zhi)。 
         * str4與(yu)str5地址(zhi)當然不一樣了。 
         *  
         * 內(nei)存(cun)中(zhong)(zhong)(zhong)實際(ji)上有五個字(zi)(zi)(zi)符串(chuan)對(dui)(dui)(dui)(dui)(dui)象: 
         *       三個拘(ju)(ju)留(liu)字(zi)(zi)(zi)符串(chuan)對(dui)(dui)(dui)(dui)(dui)象、一個String對(dui)(dui)(dui)(dui)(dui)象和一個StringBuilder對(dui)(dui)(dui)(dui)(dui)象。 
         */  
        String str2 = "ab";  //1個對(dui)象(xiang)  
        String str3 = "cd";  //1個對(dui)象                                         
        String str4 = str2+str3;                                        
        String str5 = "abcd";    
        System.out.println("str4 = str5 : " + (str4==str5)); // false  
        //↑------------------------------------------------------over  
        /** 
         * 情景五: 
         *  JAVA編譯器對string + 基本類型/常(chang)(chang)量(liang) 是當成常(chang)(chang)量(liang)表達式(shi)直接(jie)求值來優化的(de)。 
         *  運行期(qi)的(de)兩個string相加,會(hui)產(chan)生新的(de)對象的(de),存儲在堆(heap)中 
         */  
        String str6 = "b";  
        String str7 = "a" + str6;  
        String str67 = "ab";  
        System.out.println("str7 = str67 : "+ (str7 == str67));  
        //↑str6為變量,在運(yun)行期才會被(bei)解析。  
        final String str8 = "b";  
        String str9 = "a" + str8;  
        String str89 = "ab";  
        System.out.println("str9 = str89 : "+ (str9 == str89));  
        //↑str8為(wei)常量變量,編譯期會被優化  
        //↑------------------------------------------------------over  
    } 

 

總結:

 

1.String類初始化后是不可變的(immutable)

這一說又要說很多,大家只要知道String的實例一旦生成就不會再改變了,比如說:String str=”kv”+”ill”+” “+”ans”; 就是有4個字符串常量,首先”kv”和”ill”生成了”kvill”存在內存中,然后”kvill”又和” ” 生成 “kvill “存在內存中,最后又和生成了”kvill ans”;并把這個字符串的地址賦給了str,就是因為String的”不可變”產生了很多臨時變量,這也就是為什么建議用StringBuffer的原 因了,因為StringBuffer是可改變的。 

  下面是一些String相關的常見問題: 

  String中的final用法和理解 
  final StringBuffer a = new StringBuffer("111"); 
  final StringBuffer b = new StringBuffer("222"); 
  a=b;//此句編譯不通過  final StringBuffer a = new StringBuffer("111"); 
  a.append("222");// 編譯通過 

  可見,final只對引用的"值"(即內存地址)有效,它迫使引用只能指向初始指向的那個對象,改變它的指向會導致編譯期錯誤。至于它所指向的對象的變化,final是不負責的。 

2.代碼(ma)中(zhong)的(de)字(zi)符串常(chang)量(liang)在(zai)編譯的(de)過程中(zhong)收集并放在(zai)class文件的(de)常(chang)量(liang)區(qu)中(zhong),如(ru)"123"、"123"+"456"等,含有變量(liang)的(de)表達式不會收錄(lu),如(ru)"123"+a。

3.JVM在(zai)加(jia)載類的(de)(de)(de)時(shi)候,根據常量區中的(de)(de)(de)字(zi)(zi)符串生成常量池(chi),每個(ge)字(zi)(zi)符序列如(ru)"123"會(hui)生成一個(ge)實例放(fang)在(zai)常量池(chi)里(li),這個(ge)實例是不在(zai)堆(dui)里(li)的(de)(de)(de),也不會(hui)被GC,這個(ge)實例的(de)(de)(de)value屬性(xing)從源碼的(de)(de)(de)構造函數(shu)看應該是用new創建(jian)數(shu)組置入123的(de)(de)(de),所以按我的(de)(de)(de)理解此時(shi)value存放(fang)的(de)(de)(de)字(zi)(zi)符數(shu)組地址是在(zai)堆(dui)里(li),如(ru)果有誤的(de)(de)(de)話歡迎大(da)家指正。

 

4.使用String不一定創建對象

在執行到雙引號包含字符串的語句時,如String a = "123",JVM會先到常量池里查找,如果有的話返回常量池里的這個實例的引用,否則的話創建一個新實例并置入常量池里。如果是 String a = "123" + b (假設b是"456"),前半部分"123"還是走常量池的路線,但是這個+操作符其實是轉換成[SringBuffer].Appad()來實現的,所以最終a得到是一(yi)個新的實例引(yin)用,而且a的value存(cun)(cun)放(fang)的是一(yi)個新申(shen)請的字符數組內存(cun)(cun)空間的地址(存(cun)(cun)放(fang)著"123456"),而此時"123456"在常量池(chi)中是未必存(cun)(cun)在的。

要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能并沒有被創建!而可能只是指向一個先前已經創建的對象。只有通過new()方法才能保證每次都創建一個新的對象

 

5.使用new String,一定創建對象

在(zai)執行String a = new String("123")的(de)時候,首(shou)先走常量池(chi)的(de)路線取到一(yi)個實(shi)例(li)的(de)引(yin)用(yong),然(ran)后(hou)在(zai)堆上創建一(yi)個新的(de)String實(shi)例(li),走以下構(gou)造(zao)函數給value屬性(xing)賦(fu)(fu)值(zhi),然(ran)后(hou)把實(shi)例(li)引(yin)用(yong)賦(fu)(fu)值(zhi)給a:

public String(String original) {
    int size = original.count;
    char[] originalValue = original.value;
    char[] v;
      if (originalValue.length > size) {
         // The array representing the String is bigger than the new
         // String itself.  Perhaps this constructor is being called
         // in order to trim the baggage, so make a copy of the array.
            int off = original.offset;
            v = Arrays.copyOfRange(originalValue, off, off+size);
     } else {
         // The array representing the String is the same
         // size as the String, so no point in making a copy.
        v = originalValue;
     }
    this.offset = 0;
    this.count = size;
    this.value = v;
    }

從中我們可以看到(dao),雖然是新(xin)創建(jian)了一個String的(de)實(shi)例,但是value是等于常(chang)量(liang)池中的(de)實(shi)例的(de)value,即是說(shuo)沒有new一個新(xin)的(de)字符(fu)數組來存(cun)放"123"。

如果(guo)是String a = new String("123"+b)的(de)情(qing)況,首(shou)先看回第(di)4點,"123"+b得到(dao)一個實例后,再按上(shang)面的(de)構(gou)造函數執(zhi)行。

 

6.String.intern()

String對象的(de)實(shi)(shi)例(li)(li)調用(yong)intern方法后,可(ke)以讓JVM檢查(cha)常(chang)量池(chi),如果沒(mei)有實(shi)(shi)例(li)(li)的(de)value屬性對應的(de)字符串序列比如"123"(注意是檢查(cha)字符串序列而不(bu)(bu)是檢查(cha)實(shi)(shi)例(li)(li)本身),就(jiu)將本實(shi)(shi)例(li)(li)放入常(chang)量池(chi),如果有當(dang)前(qian)實(shi)(shi)例(li)(li)的(de)value屬性對應的(de)字符串序列"123"在常(chang)量池(chi)中存在,則返回(hui)常(chang)量池(chi)中"123"對應的(de)實(shi)(shi)例(li)(li)的(de)引(yin)用(yong)而不(bu)(bu)是當(dang)前(qian)實(shi)(shi)例(li)(li)的(de)引(yin)用(yong),即使當(dang)前(qian)實(shi)(shi)例(li)(li)的(de)value也(ye)是"123"。

public native String intern();

存在于.class文件中的(de)(de)常(chang)(chang)量(liang)池(chi),在運(yun)行期被JVM裝載,并且可以(yi)擴充。String的(de)(de) intern()方法就是擴充常(chang)(chang)量(liang)池(chi)的(de)(de) 一個方法;當一個String實(shi)例str調(diao)用(yong)(yong)intern()方法時,Java 查找常(chang)(chang)量(liang)池(chi)中 是否有相同Unicode的(de)(de)字(zi)符串常(chang)(chang)量(liang),如(ru)(ru)果有,則(ze)返回(hui)其的(de)(de)引用(yong)(yong),如(ru)(ru)果沒有,則(ze)在常(chang)(chang) 量(liang)池(chi)中增加一個Unicode等于str的(de)(de)字(zi)符串并返回(hui)它的(de)(de)引用(yong)(yong);看(kan)示例就清楚(chu)了

public static void main(String[] args) {
        String s0 = "kvill"; 
        String s1 = new String("kvill"); 
        String s2 = new String("kvill"); 
        System.out.println( s0 == s1 ); //false
        System.out.println( "**********" ); 
        s1.intern(); //雖(sui)然執行了s1.intern(),但它的返(fan)回值沒有賦給s1
        s2 = s2.intern(); //把(ba)常量池中(zhong)"kvill"的引用賦給(gei)s2 
        System.out.println( s0 == s1); //flase
        System.out.println( s0 == s1.intern() ); //true//說(shuo)明(ming)s1.intern()返回的是常量池中"kvill"的引用
        System.out.println( s0 == s2 ); //true
    }

最后我再(zai)破除一(yi)(yi)(yi)個(ge)錯誤的(de)(de)理(li)解(jie):有(you)人說,“使用 String.intern() 方法則(ze)(ze)可以將(jiang)一(yi)(yi)(yi)個(ge) String 類的(de)(de)保存到一(yi)(yi)(yi)個(ge)全局 String 表(biao)(biao)(biao)中(zhong) ,如(ru)果具(ju)有(you)相同值(zhi)的(de)(de) Unicode 字符(fu)串(chuan)已(yi)(yi)經(jing)在這(zhe)個(ge)表(biao)(biao)(biao)中(zhong),那(nei)么該(gai)方法返回表(biao)(biao)(biao)中(zhong)已(yi)(yi)有(you)字符(fu)串(chuan)的(de)(de)地址,如(ru)果在表(biao)(biao)(biao)中(zhong)沒有(you)相同值(zhi)的(de)(de)字符(fu)串(chuan),則(ze)(ze)將(jiang)自(zi)(zi)己(ji)的(de)(de)地址注冊到表(biao)(biao)(biao)中(zhong)”如(ru)果我把他說的(de)(de)這(zhe)個(ge)全局的(de)(de) String 表(biao)(biao)(biao)理(li)解(jie)為(wei)常(chang)量(liang)池的(de)(de)話,他的(de)(de)最后一(yi)(yi)(yi)句(ju)話,”如(ru)果在表(biao)(biao)(biao)中(zhong)沒有(you)相同值(zhi)的(de)(de)字符(fu)串(chuan),則(ze)(ze)將(jiang)自(zi)(zi)己(ji)的(de)(de)地址注冊到表(biao)(biao)(biao)中(zhong)”是錯的(de)(de): 

public static void main(String[] args) {        
        String s1 = new String("kvill"); 
        String s2 = s1.intern(); 
        System.out.println( s1 == s1.intern() ); //false
        System.out.println( s1 + " " + s2 ); //kvill kvill
        System.out.println( s2 == s1.intern() ); //true
    }

在這個(ge)類中我們沒(mei)有聲名(ming)一個(ge)”kvill”常(chang)(chang)(chang)量,所以常(chang)(chang)(chang)量池中一開始(shi)是(shi)沒(mei)有”kvill”的(de)(de),當我們調用s1.intern()后(hou)就(jiu)在常(chang)(chang)(chang)量池中新添(tian)加了(le)一 個(ge)”kvill”常(chang)(chang)(chang)量,原來(lai)的(de)(de)不(bu)在常(chang)(chang)(chang)量池中的(de)(de)”kvill”仍然存在,也(ye)就(jiu)不(bu)是(shi)“將自己的(de)(de)地址注冊到常(chang)(chang)(chang)量池中”了(le)。 

  s1==s1.intern() 為(wei)false說(shuo)明原來的”kvill”仍然存在;s2現在為(wei)常量池中”kvill”的地址,所以有s2==s1.intern()為(wei)true。

 

 

StringBuffer與StringBuilder的區別,它們的應用場景是什么?

 

jdk的(de)實現中StringBuffer與StringBuilder都繼承(cheng)自AbstractStringBuilder,對(dui)于多線(xian)程的(de)安(an)(an)全(quan)與非安(an)(an)全(quan)看到StringBuffer中方(fang)法前面的(de)一堆(dui)synchronized就大概了(le)解了(le)。
這(zhe)(zhe)里隨便講講AbstractStringBuilder的(de)(de)(de)(de)(de)實現原理:我們知道使用StringBuffer等(deng)無非就是(shi)為(wei)了(le)提高java中字(zi)符(fu)串(chuan)連接的(de)(de)(de)(de)(de)效率,因(yin)為(wei)直接使用+進行字(zi)符(fu)串(chuan)連接的(de)(de)(de)(de)(de)話,jvm會(hui)創(chuang)建多個String對(dui)(dui)象,因(yin)此(ci)造成(cheng)一(yi)(yi)定(ding)的(de)(de)(de)(de)(de)開銷。AbstractStringBuilder中采用一(yi)(yi)個char數(shu)組(zu)來保存(cun)(cun)需要append的(de)(de)(de)(de)(de)字(zi)符(fu)串(chuan),char數(shu)組(zu)有一(yi)(yi)個初始(shi)大(da)小,當(dang)append的(de)(de)(de)(de)(de)字(zi)符(fu)串(chuan)長度超過(guo)當(dang)前(qian)char數(shu)組(zu)容(rong)量時,則對(dui)(dui)char數(shu)組(zu)進行動態擴(kuo)展,也即重(zhong)新申(shen)請一(yi)(yi)段更大(da)的(de)(de)(de)(de)(de)內(nei)存(cun)(cun)空間(jian),然后將當(dang)前(qian)char數(shu)組(zu)拷(kao)貝(bei)到新的(de)(de)(de)(de)(de)位(wei)置,因(yin)為(wei)重(zhong)新分(fen)配內(nei)存(cun)(cun)并拷(kao)貝(bei)的(de)(de)(de)(de)(de)開銷比較大(da),所以每(mei)次重(zhong)新申(shen)請內(nei)存(cun)(cun)空間(jian)都是(shi)采用申(shen)請大(da)于當(dang)前(qian)需要的(de)(de)(de)(de)(de)內(nei)存(cun)(cun)空間(jian)的(de)(de)(de)(de)(de)方式,這(zhe)(zhe)里是(shi)2倍
 
    StringBuffer 始于 JDK 1.0 ;
    StringBuilder 始于(yu) JDK 1.5 

    從 JDK 1.5 開始,帶有字符串(chuan)變量的連接操作(+),JVM 內部采(cai)用的是 
    StringBuilder 來實現的,而之(zhi)前(qian)這個操作(zuo)是采用(yong) StringBuffer 實現的。
 
我們通過(guo)一個簡單的(de)(de)程序來(lai)看其執行的(de)(de)流程:
public class Buffer {  
     public static void main(String[] args) {  
            String s1 = "aaaaa";  
            String s2 = "bbbbb";  
            String r = null;  
            int i = 3694;  
            r = s1 + i + s2;   
              
            for(int j=0;i<10;j++){  
                r+="23124";  
            }  
     }  
}  

使用命令javap -c Buffer查看其字節(jie)碼實現:

將清(qing)單(dan)1和清(qing)單(dan)2對應起來看,清(qing)單(dan)2的(de)字節碼中ldc指(zhi)令即從常量池中加載(zai)“aaaaa”字符(fu)串到棧(zhan)頂,istore_1將“aaaaa”存到變(bian)量1中,后面的(de)一樣,sipush是將一個(ge)短整型(xing)常量值(-32768~32767)推(tui)送至棧(zhan)頂,這里是常量“3694”,更多(duo)的(de)Java指(zhi)令集請(qing)查看另一篇文(wen)章(zhang)“”。
 
讓我們直接(jie)看(kan)到(dao)13,13~17是new了(le)一(yi)個(ge)StringBuffer對象并(bing)調用(yong)其初始化(hua)方(fang)法,20~21則是先通(tong)(tong)過(guo)aload_1將變量1壓到(dao)棧頂(ding),前(qian)面說過(guo)變量1放(fang)的(de)就是字符串(chuan)常量“aaaaa”,接(jie)著通(tong)(tong)過(guo)指令invokevirtual調用(yong)StringBuffer的(de)append方(fang)法將“aaaaa”拼(pin)接(jie)起來,后(hou)續的(de)24~30同理。最后(hou)在33調用(yong)StringBuffer的(de)toString函數(shu)獲得String結果并(bing)通(tong)(tong)過(guo)astore存到(dao)變量3中(zhong)。
 
看到這里可能有人會說,“既然(ran)JVM內部采用(yong)了(le)StringBuffer來連接(jie)字(zi)符串了(le),那么我(wo)們(men)自己就不用(yong)用(yong)StringBuffer,直(zhi)接(jie)用(yong)”+“就行了(le)吧(ba)!“。是么?當(dang)然(ran)不是了(le)。俗話說”存在既有它的理(li)由(you)”,讓我(wo)們(men)繼續(xu)看后面(mian)的循環(huan)對應的字(zi)節碼。
 
37~42都(dou)是進(jin)入(ru)for循(xun)環前的一些準備工作(zuo)(zuo)(zuo),37,38是將(jiang)j置為(wei)1。44這里(li)通過if_icmpge將(jiang)j與10進(jin)行比較,如果(guo)j大于10則直接(jie)跳轉到(dao)(dao)73,也即return語句退出(chu)函數;否則進(jin)入(ru)循(xun)環,也即47~66的字(zi)節碼(ma)(ma)。這里(li)我(wo)們只需看47到(dao)(dao)51就知道為(wei)什么我(wo)們要在代(dai)碼(ma)(ma)中自己使用StringBuffer來(lai)處理字(zi)符串的連(lian)接(jie)了(le),因為(wei)每次執行“+”操作(zuo)(zuo)(zuo)時jvm都(dou)要new一個StringBuffer對象來(lai)處理字(zi)符串的連(lian)接(jie),這在涉(she)及很多的字(zi)符串連(lian)接(jie)操作(zuo)(zuo)(zuo)時開(kai)銷會(hui)很大。

 

參考:

 

posted @ 2014-09-17 12:11  ^_TONY_^  閱讀(42035)  評論(10)    收藏  舉報