有關string stringbuff stringbuild 的區別


string  stringbuff stringbuild的執行效率: stringbuild>stringbuff>string

String類是不可變類,任何對String的改變都會引發新的String對象的生成;

StringBuffer是可變類,任何對它所指代的字符串的改變都不會產生新的對象,線程安全的。

StringBuilder是可變類,線性不安全的,不支持並發操作,不適合多線程中使用,但其在單線程中的性能比StringBuffer高。

棧:存放基本類型的變量數據和對象的引用。像int a = 1;  String str = "hello" ; String str1 = new String("OK") ;  棧中存放的是 a, 1, str, str1。

常量池:存放基本類型常量和字符串常量。

堆:存放所有new出來的對象。

棧中的數據大小和生命周期是可以確定的,當沒有引用指向數據時,這個數據就會自動消失。堆中的對象的由垃圾回收器負責回收,因此大小和生命周期不需要確定,具有很大的靈活性。

而對於字符串來說,其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(即指用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的對象)則存儲在堆中。對於equals相等的字符串,在常量池中是只有一份的,在堆中則有多份。

舉例看下吧:String str1="abc";
String str2="abc";
String str3="abc";
String str4=new String("abc");
String str5=new String("abc");

String str5=new String("abc");

對於淺藍色箭頭,通過new操作產生一個字符串(“abc”)時,會先去常量池中查找是否有“abc”對象,如果沒有則在常量池中創建一個此字符串對象,然后堆中再創建一個常量池中此“abc”對象的拷貝對象,所以,對於String str=new String("abc"),如果常量池中原來沒有"abc"則產生兩個對象,否則產生一個對象。

這里插入一段題外話:有 int a= 3; int b = 3;編譯器首先處理int a = 3;先是在棧中建立一個變量為a的引用,然后在棧中查找是否有字面值為3的地址,若沒找到,就在棧中開辟一個空間來存放3這個字面值的地址,然后將a指向3的地址。接下來編譯器處理int b = 3;同樣在棧中建立b的引用變量后,由於在棧中找到了有3這個字面量的值,便直接將b指向了3的地址,這樣a,b都同時指向了3這個字面量地址。如果我們接下來聲明 a = 4;那會發生什么呢?這時在編譯器內部,它會重新搜索棧中是否存在有4這個字面值,若有,則將a指向這個4字面值的地址,若沒有,則在棧中自己開辟一個,然后指向它,不會影響b的指向。

而對於String str1 = "abc"; Sring str2 = "abc"; String str3 = new String("abc"); 編譯器的處理是這樣的: 先遇到String str1 = "abc"; 首先在棧中建立一個str1的引用,然后在常量池中查找是否存放為"abc"的地址("abc"存放在常量池中,引用在棧中),找到就指向它,沒有就在常量池中造一個,然后也指向它。接着Sring str2 = "abc"; 因為常量池有了"abc",所以str2就也指向它,最后到了String str3 = new String("abc"); 先在棧中建立一個引用str3,然后去常量池中查找是否有“abc”對象,如果沒有則在常量池中創建一個此字符串對象,然后堆中再創建一個常量池中此“abc”對象的拷貝對象,所以,對於String str=new String("abc"),如果常量池中原來沒有"abc"則產生兩個對象,否則產生一個對象。

Java采用這些機制的目的也就是希望節省內存空間了,經常用到的而且又是不怎么變化的數據,就讓大家一起分享!

 

1. 首先String不屬於8種基本數據類型,String是一個final類。

1.基本數據類型,也稱原始數據類型。

  • byte,short,char,int,long,float,double,boolean他們之間的比較,應用雙等號(==),比較的是他們的值。

2.復合數據類型(類)

  • 當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,所以,除非是同一個new出來的對象,他們的比較后的結果為true,否則比較后結果為false。 JAVA當中所有的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的內存地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆內存中的存放地址了。
  • 對於復合數據類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基於他們在內存中的存放位置的地址值的,因為Object的equals方法也是用雙等號(==)進行比較的,所以比較后的結果跟雙等號(==)的結果相同。

2. new String()和new String(“”)都是申明一個新的空字符串,是空串不是null;

3. String str=”kvill”; String str=new String (“kvill”);的區別:

在這里,我們不談堆,也不談棧,只先簡單引入常量池這個簡單的概念。

常量池(constant pool)指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。它包括了關於類、方法、接口等中的常量,也包括字符串常量。 
例1:

String s0=”kvill”; String s1=”kvill”; String s2=”kv” + “ill”; System.out.println( s0==s1 ); System.out.println( s0==s2 ); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

結果為:

true 
true

首先,我們要知道Java會確保一個字符串常量只有一個拷貝。

因為例子中的s0和s1中的”kvill”都是字符串常量,它們在編譯期就被確定了,所以s0==s1為true;而”kv”和”ill”也都是字符 串常量,當一個字符串由多個字符串常量連接而成時,它自己肯定也是字符串常量,所以s2也同樣在編譯期就被解析為一個字符串常量,所以s2也是常量池中 ”kvill”的一個引用。

所以我們得出s0==s1==s2;

用new String() 創建的字符串不是常量,不能在編譯期就確定,所以new String() 創建的字符串不放入常量池中,它們有自己的地址空間。

例2:

String s0=”kvill”; String s1=new String(”kvill”); String s2=”kv” + new String(“ill”); System.out.println( s0==s1 ); System.out.println( s0==s2 ); System.out.println( s1==s2 ); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

結果為:

false 
false 
false

例2中s0還是常量池中”kvill”的應用,s1因為無法在編譯期確定,所以是運行時創建的新對象”kvill”的引用,s2因為有后半部分new String(“ill”)所以也無法在編譯期確定,所以也是一個新創建對象”kvill”的應用;明白了這些也就知道為何得出此結果了。

4. String.intern():

再補充介紹一點:存在於.class文件中的常量池,在運行期被JVM裝載,並且可以擴充。String的intern()方法就是擴充常量池的一個 方法;當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字符串常量,如果有,則返回其的引用, 如果沒有,則在常量池中增加一個Unicode等於str的字符串並返回它的引用;看例3就清楚了

例3:

String s0= "kvill"; String s1=new String("kvill"); String s2=new String("kvill"); s1.intern(); s2=s2.intern(); //把常量池中"kvill"的引用賦給s2 System.out.println( s0==s1); System.out.println( s0==s1.intern() ); System.out.println( s0==s2 );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

結果為:

false //雖然執行了s1.intern(),但它的返回值沒有賦給s1 
true //說明s1.intern()返回的是常量池中”kvill”的引用 
true

最后我再破除一個錯誤的理解:

  • 有人說,“使用String.intern()方法則可以將一個String類的保存到一個全局String表中,如果具有相同值的Unicode字 符串已經在這個表中,那么該方法返回表中已有字符串的地址,如果在表中沒有相同值的字符串,則將自己的地址注冊到表中“如果我把他說的這個全局的 String表理解為常量池的話,他的最后一句話,“如果在表中沒有相同值的字符串,則將自己的地址注冊到表中”是錯的:

例4:

String s1=new String("kvill"); String s2=s1.intern(); System.out.println( s1==s1.intern() ); System.out.println( s1+" "+s2 ); System.out.println( s2==s1.intern() ); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

結果:

false 
kvill kvill 
true

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

  • s1==s1.intern()為false說明原來的“kvill”仍然存在;

  • s2現在為常量池中“kvill”的地址,所以有s2==s1.intern()為true。

5. 關於equals()和==:

equals()對於String來說就是比較兩字符串的Unicode序列是否相當,如果相等返回true,原因是String類已經重寫了equals() 方法,你可以去看下源碼;而==是比較兩字符串的地址是否相同,也就是是否是同一個字符串的引用。

6. 關於String是不可變的

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM