亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

String、StringBuffer、Stringbuild區別、性能比較

2019-11-11 03:55:52
字體:
來源:轉載
供稿:網友

原文地址:http://www.iteye.com/topic/522167

作者:每次上網沖杯java時,都能看到關于String無休無止的爭論。還是覺得有必要讓這個討厭又很可愛的String美眉,赤裸裸的站在我們這些Java色狼面前了。嘿嘿....眾所周知,String是由字符組成的串,在程序中使用頻率很高。Java中的String是一個類,而并非基本數據類型。 不過她卻不是普通的類哦?。。?/p>

 

【鏡頭1】 String對象的創建      1、關于類對象的創建,很普通的一種方式就是利用構造器,String類也不例外:String s=new String("Hello world"); 問題是參數"Hello world"是什么東西,也是字符串對象嗎?莫非用字符串對象創建一個字符串對象?      2、當然,String類對象還有一種大家都很喜歡的創建方式:String s="Hello world"; 但是有點怪呀,怎么與基本數據類型的賦值操作(int i=1)很像呀?

      在開始解釋這些問題之前,我們先引入一些必要的知識:★ Java class文件結構和常量池      我們都知道,Java程序要運行,首先需要編譯器將源代碼文件編譯成字節碼文件(也就是.class文件)。然后在由JVM解釋執行。      class文件是8位字節的二進制流。這些二進制流的涵義由一些緊湊的有意義的項組成。比如class字節流中最開始的4個字節組成的項叫做魔數(magic),其意義在于分辨class文件(值為0xCAFEBABE)與非class文件。class字節流大致結構如下圖左側。

                               

      其中,在class文件中有一個非常重要的項——常量池。這個常量池專門放置源代碼中的符號信息(并且不同的符號信息放置在不同標志的常量表中)。如上圖右側是HelloWorld代碼中的常量表(HelloWorld代碼如下),其中有四個不同類型的常量表(四個不同的常量池入口)。關于常量池的具體細節,請參照我的博客《Class文件內容及常量池》

Java代碼  收藏代碼public class HelloWorld{      void hello(){          System.out.PRintln("Hello world");      }  }  

      通過上圖可見,代碼中的"Hello world"字符串字面值被編譯之后,可以清楚的看到存放在了class常量池中的字符串常量表中(上圖右側紅框區域)。

 

JVM運行class文件

      源代碼編譯成class文件之后,JVM就要運行這個class文件。它首先會用類裝載器加載進class文件。然后需要創建許多內存數據結構來存放class文件中的字節數據。比如class文件對應的類信息數據、常量池結構、方法中的二進制指令序列、類方法與字段的描述信息等等。當然,在運行的時候,還需要為方法創建棧幀等。這么多的內存結構當然需要管理,JVM會把這些東西都組織到幾個“運行時數據區”中。這里面就有我們經常說的“方法區”、“”、“Java棧”等。詳細請參見我的博客《Java 虛擬機體系結構》。

 

      上面我們提到了,在Java源代碼中的每一個字面值字符串,都會在編譯成class文件階段,形成標志號為8(CONSTANT_String_info)的常量表。當JVM加載 class文件的時候,會為對應的常量池建立一個內存數據結構,并存放在方法區中。同時JVM會自動為CONSTANT_String_info常量表中的字符串常量字面值在堆中創建新的String對象(intern字符串對象,又叫拘留字符串對象)。然后把CONSTANT_String_info常量表的入口地址轉變成這個堆中String對象的直接地址(常量池解析)。 

 

      這里很關鍵的就是這個拘留字符串對象。源代碼中所有相同字面值的字符串常量只可能建立唯一一個拘留字符串對象。實際上JVM是通過一個記錄了拘留字符串引用的內部數據結構來維持這一特性的。在Java程序中,可以調用String的intern()方法來使得一個常規字符串對象成為拘留字符串對象。我們會在后面介紹這個方法的。

 

操作碼助憶符指令      有了上面闡述的兩個知識前提,下面我們將根據二進制指令來區別兩種字符串對象的創建方式:  

      (1) String s=new String("Hello world");編譯成class文件后的指令(在myeclipse中查看):

Class字節碼指令集代碼  收藏代碼0  new java.lang.String [15]  //在堆中分配一個String類對象的空間,并將該對象的地址堆入操作數棧。  3  dup //復制操作數棧頂數據,并壓入操作數棧。該指令使得操作數棧中有兩個String對象的引用值。  4  ldc <String "Hello world"> [17] //將常量池中的字符串常量"Hello world"指向的堆中拘留String對象的地址壓入操作數棧  6  invokespecial java.lang.String(java.lang.String) [19] //調用String的初始化方法,彈出操作數棧棧頂的兩個對象地址,用拘留String對象的值初始化new指令創建的String對象,然后將這個對象的引用壓入操作數棧  9  astore_1 [s] // 彈出操作數棧頂數據存放在局部變量區的第一個位置上。此時存放的是new指令創建出的,已經被初始化的String對象的地址。  

      事實上,在運行這段指令之前,JVM就已經為"Hello world"在堆中創建了一個拘留字符串(值得注意的是:如果源程序中還有一個"Hello world"字符串常量,那么他們都對應了同一個堆中的拘留字符串)。然后用這個拘留字符串的值來初始化堆中用new指令創建出來的新的String對象,局部變量s實際上存儲的是new出來的堆對象地址。大家注意了,此時在JVM管理的堆中,有兩個相同字符串值的String對象:一個是拘留字符串對象,一個是new新建的字符串對象。如果還有一條創建語句String s1=new String("Hello world");堆中有幾個值為"Hello world"的字符串呢? 答案是3個,大家好好想想為什么吧!

 

      (2)將String s="Hello world";編譯成class文件后的指令:

Class字節碼指令集代碼  收藏代碼0  ldc <String "Hello world"> [15]//將常量池中的字符串常量"Hello world"指向的堆中拘留String對象的地址壓入操作數棧  2  astore_1 [str] // 彈出操作數棧頂數據存放在局部變量區的第一個位置上。此時存放的是拘留字符串對象在堆中的地址  

      和上面的創建指令有很大的不同,局部變量s存儲的是早已創建好的拘留字符串的堆地址。大家好好想想,如果還有一條穿件語句String s1="Hello Word";此時堆中有幾個值為"Hello world"的字符串呢?答案是1個。那么局部變量s與s1存儲的地址是否相同呢?  呵呵, 這個你應該知道了吧。

 

 

★ 鏡頭總結:String類型脫光了其實也很普通。真正讓她神秘的原因就在于CONSTANT_String_info常量表拘留字符串對象的存在?,F在我們可以解決江湖上的許多紛爭了。

  紛爭1】關于字符串相等關系的爭論

Java代碼  收藏代碼//代碼1  String sa=new String("Hello world");            String sb=new String("Hello world");      System.out.println(sa==sb);  // false       //代碼2    String sc="Hello world";    String sd="Hello world";  System.out.println(sc==sd);  // true   

       代碼1中局部變量sa,sb中存儲的是JVM在堆中new出來的兩個String對象的內存地址。雖然這兩個String對象的值(char[]存放的字符序列)都是"Hello world"。因此"=="比較的是兩個不同的堆地址。代碼2中局部變量sc,sd中存儲的也是地址,但卻都是常量池中"Hello world"指向的堆的唯一的那個拘留字符串對象的地址。自然相等了。

  【紛爭2】 字符串“+”操作的內幕

Java代碼  收藏代碼//代碼1  String sa = "ab";                                          String sb = "cd";                                       String sab=sa+sb;                                      String s="abcd";  System.out.println(sab==s); // false  //代碼2  String sc="ab"+"cd";  String sd="abcd";  System.out.println(sc==sd); //true  

       代碼1中局部變量sa,sb存儲的是堆中兩個拘留字符串對象的地址。而當執行sa+sb時,JVM首先會在堆中創建一個StringBuilder類,同時用sa指向的拘留字符串對象完成初始化,然后調用append方法完成對sb所指向的拘留字符串的合并操作,接著調用StringBuilder的toString()方法在堆中創建一個String對象,最后將剛生成的String對象的堆地址存放在局部變量sab中。而局部變量s存儲的是常量池中"abcd"所對應的拘留字符串對象的地址。sab與s地址當然不一樣了。這里要注意了,代碼1的堆中實際上有五個字符串對象:三個拘留字符串對象、一個String對象和一個StringBuilder對象。      代碼2中"ab"+"cd"會直接在編譯期就合并成常量"abcd",因此相同字面值常量"abcd"所對應的是同一個拘留字符串對象,自然地址也就相同。

 

 

 

 

【鏡頭二】  String三姐妹(String,StringBuffer,StringBuilder)        String扒的差不多了。但他還有兩個妹妹StringBuffer,StringBuilder長的也不錯哦!我們也要下手了:                           String(大姐,出生于JDK1.0時代)          不可變字符序列                           StringBuffer(二姐,出生于JDK1.0時代)    線程安全的可變字符序列                           StringBuilder(小妹,出生于JDK1.5時代)   非線程安全的可變字符序列

 

★StringBuffer與String的可變性問題。         我們先看看這兩個類的部分源代碼:

Java代碼  收藏代碼//String   public final class String  {          private final char value[];             public String(String original) {                // 把原字符串original切分成字符數組并賦給value[];           }  }    //StringBuffer   public final class StringBuffer extends AbstractStringBuilder  {           char value[]; //繼承了父類AbstractStringBuilder中的value[]           public StringBuffer(String str) {                   super(str.length() + 16); //繼承父類的構造器,并創建一個大小為str.length()+16的value[]數組                   append(str); //將str切分成字符序列并加入到value[]中          }  }  

      很顯然,String和StringBuffer中的value[]都用于存儲字符序列。但是,      (1) String中的是常量(final)數組,只能被賦值一次。      比如:new String("abc")使得value[]={'a','b','c'},之后這個String對象中的value[]再也不能改變了。這也正是大家常說的,String是不可變的原因。         注意:這個對初學者來說有個誤區,有人說String str1=new String("abc"); str1=new String("cba");不是改變了字符串str1嗎?那么你有必要先搞懂對象引用和對象本身的區別。這里我簡單的說明一下,對象本身指的是存放在堆空間中的該對象的實例數據(非靜態非常量字段)。而對象引用指的是堆中對象本身所存放的地址,一般方法區和Java棧中存儲的都是對象引用,而非對象本身的數據。

      (2) StringBuffer中的value[]就是一個很普通的數組,而且可以通過append()方法將新字符串加入value[]末尾。這樣也就改變了value[]的內容和大小了。

      比如:new StringBuffer("abc")使得value[]={'a','b','c','',''...}(注意構造的長度是str.length()+16)。如果再將這個對象append("abc"),那么這個對象中的value[]={'a','b','c','a','b','c',''....}。這也就是為什么大家說StringBuffer是可變字符串的涵義了。從這一點也可以看出,StringBuffer中的value[]完全可以作為字符串的緩沖區功能。其累加性能是很不錯的,在后面我們會進行比較。     總結,討論String和StringBuffer可不可變。本質上是指對象中的value[]字符數組可不可變,而不是對象引用可不可變。

 

 

★StringBuffer與StringBuilder的線程安全性問題      StringBuffer和StringBuilder可以算是雙胞胎了,這兩者的方法沒有很大區別。但在線程安全性方面,StringBuffer允許多線程進行字符操作。這是因為在源代碼中StringBuffer的很多方法都被關鍵字synchronized修飾了,而StringBuilder沒有。      有多線程編程經驗的程序員應該知道synchronized。這個關鍵字是為線程同步機制設定的。我簡要闡述一下synchronized的含義:      每一個類對象都對應一把鎖,當某個線程A調用類對象O中的synchronized方法M時,必須獲得對象O的鎖才能夠執行M方法,否則線程A阻塞。一旦線程A開始執行M方法,將獨占對象O的鎖。使得其它需要調用O對象的M方法的線程阻塞。只有線程A執行完畢,釋放鎖后。那些阻塞線程才有機會重新調用M方法。這就是解決線程同步問題的鎖機制。      了解了synchronized的含義以后,大家可能都會有這個感覺。多線程編程中StringBuffer比StringBuilder要安全多了,事實確實如此。如果有多個線程需要對同一個字符串緩沖區進行操作的時候,StringBuffer應該是不二選擇。      注意:是不是String也不安全呢?事實上不存在這個問題,String是不可變的。線程對于堆中指定的一個String對象只能讀取,無法修改。試問:還有什么不安全的呢?

 

★String和StringBuffer的效率問題(這可是個熱門話題呀!)      首先說明一點:StringBuffer和StringBuilder可謂雙胞胎,StringBuilder是1.5新引入的,其前身就是StringBuffer。StringBuilder的效率比StringBuffer稍高,如果不考慮線程安全,StringBuilder應該是首選。另外,JVM運行程序主要的時間耗費是在創建對象和回收對象上。

      我們用下面的代碼運行1W次字符串的連接操作,測試String,StringBuffer所運行的時間。

Java代碼  收藏代碼//測試代碼  public class RunTime{      public static void main(String[] args){             ● 測試代碼位置1            long beginTime=System.currentTimeMillis();            for(int i=0;i<10000;i++){                   ● 測試代碼位置2            }            long endTime=System.currentTimeMillis();            System.out.println(endTime-beginTime);         }  }  

(1) String常量與String變量的"+"操作比較        ▲測試①代碼:     (測試代碼位置1)  String str="";                                  (測試代碼位置2)  str="Heart"+"Raid";            [耗時:  0ms]                    ▲測試②代碼        (測試代碼位置1)  String s1="Heart";                                                           String s2="Raid";                                                           String str="";                                  (測試代碼位置2)  str=s1+s2;            [耗時:  15—16ms]      結論:String常量的“+連接”  稍優于  String變量的“+連接”。      原因:測試①的"Heart"+"Raid"在編譯階段就已經連接起來,形成了一個字符串常量"HeartRaid",并指向堆中的拘留字符串對象。運行時只需要將"HeartRaid"指向的拘留字符串對象地址取出1W次,存放在局部變量str中。這確實不需要什么時間。               測試②中局部變量s1和s2存放的是兩個不同的拘留字符串對象的地址。然后會通過下面三個步驟完成“+連接”:                                1、StringBuilder temp=new StringBuilder(s1),                                2、temp.append(s2);                                3、str=temp.toString();               我們發現,雖然在中間的時候也用到了append()方法,但是在開始和結束的時候分別創建了StringBuilder和String對象??上攵赫{用1W次,是不是就創建了1W次這兩種對象呢?不劃算。

     但是,String變量的"+連接"操作比String常量的"+連接"操作使用的更加廣泛。 這一點是不言而喻的。    

(2)String對象的"累+"連接操作與StringBuffer對象的append()累和連接操作比較。          ▲測試①代碼:     (代碼位置1)  String s1="Heart";                                                       String s="";                                    (代碼位置2)  s=s+s1;             [耗時:  4200—4500ms]                       ▲測試②代碼        (代碼位置1)  String s1="Heart";                                                       StringBuffer sb=new StringBuffer();                                    (代碼位置2) sb.append(s1);             [耗時:  0ms(當循環100000次的時候,耗時大概16—31ms)]         結論:大量字符串累加時,StringBuffer的append()效率遠好于String對象的"累+"連接         原因:測試①中的s=s+s1,JVM會利用首先創建一個StringBuilder,并利用append方法完成s和s1所指向的字符串對象值的合并操作,接著調用StringBuilder的toString()方法在堆中創建一個新的String對象,其值為剛才字符串的合并結果。而局部變量s指向了新創建的String對象。

                  因為String對象中的value[]是不能改變的,每一次合并后字符串值都需要創建一個新的String對象來存放。循環1W次自然需要創建1W個String對象和1W個StringBuilder對象,效率低就可想而知了。

                  測試②中sb.append(s1);只需要將自己的value[]數組不停的擴大來存放s1即可。循環過程中無需在堆中創建任何新的對象。效率高就不足為奇了。      

 

鏡頭總結:

    (1) 在編譯階段就能夠確定的字符串常量,完全沒有必要創建String或StringBuffer對象。直接使用字符串常量的"+"連接操作效率最高。

    (2) StringBuffer對象的append效率要高于String對象的"+"連接操作。

    (3) 不停的創建對象是程序低效的一個重要原因。那么相同的字符串值能否在堆中只創建一個String對象那。顯然拘留字符串能夠做到這一點,除了程序中的字符串常量會被JVM自動創建拘留字符串之外,調用String的intern()方法也能做到這一點。當調用intern()時,如果常量池中已經有了當前String的值,那么返回這個常量指向拘留對象的地址。如果沒有,則將String值加入常量池中,并創建一個新的拘留字符串對象。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美一级视频免费在线观看| 欧美天天综合色影久久精品| 国产一区二区三区18| 中文字幕成人精品久久不卡| 国产成人在线视频| 91九色国产社区在线观看| 亚洲天堂av图片| 久久精品国产成人| 日本一本a高清免费不卡| 亚洲成人亚洲激情| 红桃视频成人在线观看| 亚洲色图在线观看| 亚洲永久在线观看| 久久影视免费观看| 91九色蝌蚪国产| 国产亚洲人成a一在线v站| 亚洲精品视频二区| 蜜月aⅴ免费一区二区三区| 欧美老肥婆性猛交视频| 欧美日韩美女在线| 欧美国产日韩中文字幕在线| 亚洲人成在线观| 亚洲天堂日韩电影| 日韩精品极品视频| 亚洲片在线观看| 亚洲成人精品久久久| 欧美日韩在线第一页| 国产精品精品久久久久久| 日韩视频在线免费观看| 高清一区二区三区日本久| 在线观看精品国产视频| 欧美日韩中文字幕在线视频| 国产精品视频久久久久| 成人av在线亚洲| 91成人在线视频| 欧洲美女免费图片一区| 操91在线视频| 亚洲一区二区黄| 97久久精品视频| 日韩高清电影免费观看完整| 成人在线中文字幕| 久久av在线看| 亚洲电影免费观看高清| 伊人亚洲福利一区二区三区| 欧美国产亚洲视频| 欧美成人四级hd版| 日韩欧美999| 性色av一区二区三区免费| 欧美激情综合色综合啪啪五月| 北条麻妃一区二区三区中文字幕| 奇米四色中文综合久久| 国产精品欧美日韩| 2020欧美日韩在线视频| 一区二区福利视频| 丁香五六月婷婷久久激情| 亚洲一区二区三区乱码aⅴ| 成人精品一区二区三区电影免费| 亚洲欧洲av一区二区| 日韩中文字幕在线视频| 成人黄色短视频在线观看| 国产视频亚洲视频| 国产精品久久久久久久美男| 亚洲精品成人久久久| 一本一本久久a久久精品牛牛影视| 精品国产美女在线| 欧美第一黄色网| 日韩一二三在线视频播| 国产99久久精品一区二区永久免费| 精品国产乱码久久久久酒店| 亚洲视频axxx| 国产精品白丝jk喷水视频一区| 在线一区二区日韩| 亚洲一区二区自拍| 欧美成人激情视频免费观看| 成人免费观看49www在线观看| 欧美国产日韩一区| 国产成人精品电影久久久| 欧美一级淫片播放口| 26uuu亚洲国产精品| 在线播放国产一区中文字幕剧情欧美| 国产欧美日韩中文字幕在线| 日韩av电影中文字幕| 岛国av午夜精品| 亚洲欧美国产高清va在线播| 亚洲人成在线电影| 亚洲va久久久噜噜噜| 亚洲最大激情中文字幕| 日韩精品极品视频免费观看| 日韩久久精品电影| 国产精自产拍久久久久久| 国产z一区二区三区| 黄网站色欧美视频| 亚洲最大成人网色| 久久夜色精品国产欧美乱| 欧美日韩一区二区免费在线观看| 日韩a**中文字幕| 亚洲在线视频福利| 欧美性高潮床叫视频| 欧美日本啪啪无遮挡网站| 亚洲精品按摩视频| 国产日韩精品入口| 欧美成人激情视频| 日韩在线观看视频免费| 欧美高跟鞋交xxxxxhd| 中文字幕亚洲情99在线| 韩国三级电影久久久久久| 日韩有码在线电影| 成人精品一区二区三区电影黑人| 国产精品黄色影片导航在线观看| 国产视频精品免费播放| 日韩精品免费视频| 久久久人成影片一区二区三区| 日韩av中文字幕在线免费观看| 日韩av在线免费| 日韩成人中文字幕在线观看| 91久久夜色精品国产网站| 最近中文字幕mv在线一区二区三区四区| 最近的2019中文字幕免费一页| 亚洲成色999久久网站| 久久久久久中文字幕| 色先锋资源久久综合5566| 精品视频在线播放| 最新日韩中文字幕| 亚洲自拍小视频免费观看| 98视频在线噜噜噜国产| 精品久久久999| 日韩av电影在线免费播放| 久久69精品久久久久久国产越南| 日韩免费中文字幕| 欧美激情网站在线观看| 91精品国产综合久久香蕉的用户体验| 中文字幕av一区| 91久久综合亚洲鲁鲁五月天| 亚洲色图15p| 亚洲伦理中文字幕| 欧美在线视频在线播放完整版免费观看| 欧美重口另类videos人妖| 国产美女久久精品| 久久中文字幕在线| 国内精品模特av私拍在线观看| 欧美日韩视频免费播放| 美乳少妇欧美精品| 亚洲图片欧美午夜| 欧美色视频日本高清在线观看| 欧美性xxxx在线播放| 精品视频在线播放色网色视频| 亚洲18私人小影院| 国内精品久久久久影院优| 91精品免费视频| 日韩在线观看成人| 最近2019中文字幕大全第二页| 亚洲人成网站在线播| 91av在线网站| 中文字幕日韩欧美| 亚洲偷熟乱区亚洲香蕉av| 黄色一区二区三区| 夜夜狂射影院欧美极品| 欧美极品美女视频网站在线观看免费| 国产欧美日韩丝袜精品一区| 成人网在线观看| 26uuu亚洲国产精品| 超碰日本道色综合久久综合| 亚洲欧美国产精品va在线观看| 欧美日韩精品在线视频|