Java中String類學習總結


java中String類的使用頻率非常高,本人在學習此模塊時,認為下列幾點知識值得注意:

一、String是不可變對象

      java.lang.String類使用了final修飾,不能被繼承。Java程序中的所有字面值,即雙引號括起的字符串,如"abc",都是作為String類的實例實現的。String是常量,其對象一旦構造就不能再被改變。換句話說,String對象是不可變的,每一個看起來會修改String值的方法,實際上都是創造了一個全新的String對象,以包含修改后的字符串內容。而最初的String對象則絲毫未動。String對象具有只讀特性,指向它的任何引用都不可能改變它的值,因此,也不會對其他的引用有什么影響。但是字符串引用可以重新賦值。java字符串在內存中采用unicode編碼方式,任何一個字符對應兩個字節的定長編碼,即任何一個字符(無論中文還是英文)都算一個字符長度,占用兩個字節。

例1:

public class Immutable {
      public static String upcase(String s) {
           return s.toUpperCase();
      }
      public static void main(String[ ] args) {
           String str1= "Hello World";
           System.out.println(str1);  //Hello World
           String str2 = upcase(str1);
           System.out.println(str2);  //HELLO WORLD
           System.out.println(str1);  //Hello World
     }
}

   當把str1傳遞給upcase()方法時,實際傳遞的是引用的一個拷貝。其實,每當把String對象作為方法的參數時,都會復制一份引用,而該引用所指的對象其實一直待在單一的物理位置上,從未動過。

二、String常量池

     常量池(constant pool)指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。它包括了關於類、方法、接口等中的常量,也包括字符串常量。Java為了提高性能,靜態字符串(字面量/常量/常量連接的結果)在常量池中創建,並盡量使用同一個對象,重用靜態字符串。對於重復出現的字符串直接量,JVM會首先在常量池中查找,如果常量池中存在即返回該對象

例2:

 
        
public class test1 {
    public static void main(String[] args){
        String str1 = "Hello";
        //不會創建新的String對象,而是使用常量池中已有的"Hello",
        String str2 = "Hello";
        System.out.println(str1 == str2); //true
        //使用new關鍵字會創建新的String對象
        String str3 = new String("Hello");
        System.out.println(str1 == str3); //false 
    }
}

 

三、String、StringBuffer和StringBuilder的區別

1.對象的可變與不可變

  String類中使用字符數組來保存數據,因為有“final”修飾符,所以string對象是不可變的。如下:

    private final char value[];

  StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字符數組保存數據,這兩種對象都是可變的。如下:

    char[] value;

2.是否是線程安全

  String中的對象是不可變的,也就可以理解為常量,所以是線程安全

  AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。

  StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的看如下源碼

1  public synchronized StringBuffer reverse() {
2      super.reverse();
3      return this;
4  }
5
6  public int indexOf(String str) {
7      return indexOf(str, 0);        //存在 public synchronized int indexOf(String str, int fromIndex) 方法
8  }

  StringBuilder並沒有對方法進行加同步鎖,所以是非線程安全的

 3.StringBuilder與StringBuffer共同點

  StringBuilder與StringBuffer有公共的抽象父類AbstractStringBuilder。

  抽象類與接口的一個區別是:抽象類中可以定義一些子類的公共方法,子類只需要增加新的功能,不需要重復寫已經存在的方法;而接口中只是對方法的申明和常量的定義。

  StringBuilder、StringBuffer的方法都會調用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer會在方法上加synchronized關鍵字,進行同步。

如果程序不是多線程的,那么使用StringBuilder效率高於StringBuffer。

四、創建了幾個對象的問題

1 String str1 = "abc";
2 String str2 = new String("abc");

      對於1中的 String str1 = "abc",首先會檢查字符串常量池中是否含有字符串abc,如果有則直接指向,如果沒有則在字符串常量池中添加abc字符串並指向它.所以這種方法最多創建一個對象,有可能不創建對象。

      對於2中的String str2 = new String("abc"),首先會在堆內存中申請一塊內存存儲字符串abc,str2指向其內存塊對象。同時還會檢查字符串常量池中是否含有abc字符串,若沒有則添加abc到字符串常量池中。所以 new String()可能會創建兩個對象。

      所以如果以上兩行代碼在同一個程序中,則1中創建了1個對象,2中創建了1個對象。如果將這兩行代碼的順序調換一下,則1 String str2 = new String("abc")創建了兩個對象,而2 String str1 = "abc"沒有創建對象。

看看下面的代碼創建了多少個對象:

1     String temp="apple";  
2     for(int i=0;i<1000;i++) {  
3           temp=temp+i;  
4     }  

答案:1001個對象。

下面的代碼創建了多少個對象:

1     String temp=new String("apple")  
2     for(int i=0;i<1000;i++) {  
3            temp=temp+i;  
4     }  

答案:1002個對象。

五、String的匹配相等問題

      使用String類經常需要對兩個字符串進行對比,看是否相等。有==和equals兩種選擇,這兩者方法區別很大:

             比對象的內容是否相等使用equals(),比較兩個引用是否指向同一個對象時用==;equals()是看內容是否相等,比較好理解。而==是看是否屬於同一個對象。
首先還要明白這個概念:常量池在java用於保存在 編譯期已確定的,已編譯的class文件中的一份數據。主要看編譯期字符串能否確定。
看看下面幾個例子:
test1:
1 String ok="ok";  
2 String ok1=new String("ok");  
3 System.out.println(ok==ok1);//fasle 

ok指向字符串常量池,ok1指向new出來的堆內存塊,new的字符串在編譯期是無法確定的。所以輸出false。

test2:

1 String ok="apple1";  
2 String ok1="apple"+1;  
3 System.out.println(ok==ok1);//true 

編譯期ok和ok1都是確定的,字符串都為apple1,所以ok和ok1都指向字符串常量池里的字符串apple1。指向同一個對象,所以為true。

test3:

1 String ok="apple1";  
2 int temp=1;  
3 String ok1="apple"+temp;  
4 System.out.println(ok==ok1);//false

主要看ok和ok1能否在編譯期確定,ok是確定的,放進並指向常量池,而ok1含有變量導致不確定,所以不是同一個對象.輸出false。

test4:

1 String ok="apple1";  
2 final int temp=1;  
3 String ok1="apple"+temp;  
4 System.out.println(ok==ok1);//true 

ok確定,加上final后使得ok1也在編譯期能確定,所以輸出true。

test5:

 1 public static void main(String[] args) {    
 2     String ok="apple1";  
 3     final int temp=getTemp();  
 4     String ok1="apple"+temp;  
 5     System.out.println(ok==ok1);//false       
 6 }  
 7   
 8 public static int getTemp(){  
 9     return 1;  
10 }  

ok一樣是確定的。而ok1不能確定,需要運行代碼獲得temp,所以不是同一個對象,輸出false。

String的intern()方法

當調用 intern()方法時,如果常量池中已經包含一個等於此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回常量池中的字符串;否則,將此 String 對象添加到常量池中,並且返回此 String 對象的引用。

看以下幾個例子:

test6:

public class test6 {

    private static String a = "ab";
    public static void main(String[] args){
        String s1 = "a";
        String s2 = "b";
        String s = s1 + s2;//+的用法
        System.out.println(s == a); //flase
        System.out.println(s.intern() == a);// true (intern的含義)
    }

}

test7:

 1 public class test6 {
 2     private static String a = new String("ab");
 3     public static void main(String[] args){
 4         String s1 = "a";
 5         String s2 = "b";
 6         String s = s1 + s2;
 7         System.out.println(s == a); //flase
 8         System.out.println(s.intern() == a);//false
 9         System.out.println(s.intern() == a.intern()); //true
10     } 
11 }

 

 


免責聲明!

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



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