1. 程式人生 > 程式設計 >String例項化及static final修飾符實現方法解析

String例項化及static final修飾符實現方法解析

String兩種例項化方式

一種是通過雙引號直接賦值的方式,另外一種是使用標準的new呼叫構造方法完成例項化。如下:

  String str = "abcd";
  String str = new String("1234);

第一種方法:

  使用直接賦值後,只要是以後宣告的字串內容相同,則不會再開闢新的記憶體空間。對於String的以上操作,在java中稱為共享設計。這種設計思路是,在java中形成一個字串物件池,在這個字串物件中儲存多個字串物件,新例項化的物件如果已經在池中定義了,則不再重新定義,而從池中直接取出繼續使用。String就是因為採用了這樣的設計,所以當內容重複時,會將物件指向已存在的例項空間。

  一個雙引號包含字串就是一個String類的匿名物件,但是這種方式使用String不一定建立新物件。在執行到這個字串的語句時,如String a = "123",JVM會先到常量池裡查詢,如果有的話返回常量池裡的這個例項的引用,否則的話建立一個新例項並置入常量池裡。

第二種方法:

  使用new關鍵字,不管如何都會再開闢一個新的空間。

  new建立字串時首先檢視池中是否有相同值的字串,如果有,則拷貝一份到堆中,然後返回堆中的地址;如果池中沒有,則在堆中建立一份,然後返回堆中的地址(注意,此時不需要從堆中複製到池中,否則,將使得堆中的字串永遠是池中的子集,導致浪費池的空間)!

String例項化的時機

(1)單獨使用""引號建立的字串都是常量,編譯期就已經確定儲存到String Pool中;

(2)使用new String("")建立的物件會儲存到堆區(heap)中,是執行期新建立的;

(3)使用只包含常量的字串連線符如"aa" + "aa"建立的也是常量,編譯期就能確定,已經確定儲存到String Pool中;

(4)使用包含變數的字串連線符如"aa" + s1建立的物件是執行期才建立的,儲存在堆區(heap)中;

  注意:上面第(3)句話,編譯後合併的字串會儲存在JVM的字串池中,而不是再生成的class檔案中把字串合併。
  String s = "a" + "b" + "c"; 建立的是一個物件,而不是是四個物件,在字串常量池中只生成一個字串物件

字串池的優缺點

  字串池的優點就是避免了相同內容的字串的建立,節省了記憶體,省去了建立相同字串的時間,同時提升了效能;另一方面,字串池的缺點就是犧牲了JVM在常量池中遍歷物件所需要的時間,不過其時間成本相比而言比較低。

static final修飾的字串好嗎?

工作後發現,大型的專案裡,常常會見到定義字串使用 private static final String = "abc" 的方式。這種方式有好處嗎?

  首先使用直接賦值的字串的方式,字串會在編譯期生成在字串池中。

  然後final標記的變數(成員變數或區域性變數)即成為常量,只能賦值一次。它應該不影響記憶體的分配。(檢視資料多了,說法不一,在下對此也有點懷疑了,如果final影響記憶體分配,煩請各位大俠告知)

  最後看static修飾符:

static修飾符能夠與屬性、方法和內部類一起使用,表示靜態的。類中的靜態變數和靜態方法能夠與類名一起使用,不需要建立一個類的物件來訪問該類的靜態成員,所以,static修飾的變數又稱作“類變數”。

  “類變數”屬於類的成員,類的成員是被儲存在堆記憶體裡面的。一個類中,一個static變數只會有一個記憶體空間,即使有多個類例項,但這些類例項中的這個static變數會共享同一個記憶體空間。

  static修飾的String,會在堆記憶體中複製一份常量池中的值。所以呼叫 static final String 變數,實際上是直接呼叫堆記憶體的地址,不會遍歷字串池中的物件,節省了遍歷時間。

所以使用static final修飾的字串還是有好處的。

程式碼測試

public class Test
{
  public static final String A="ab";
  public static final String B="cd";

  public static final String C;
  public static final String D;
  static{
    C = "ab";
    D = "cd";
  }
  public static void main(String[] args) {
    String t = "abcd";//指向池

    String s1 = "ab";//指向池
    String s2 = "cd";//指向池

    String s = s1+s2;//指向堆
    System.out.println(s==t);//false

    String ss = "ab"+s2;//指向堆
    System.out.println(ss==t);//false

    String sss = "ab"+"cd";//指向池
    System.out.println(sss==t);//true

    String ssss = A+B;//指向池
    System.out.println(ssss==t);//true

    System.out.println((C+D)==t);//false
  }

}

字串物件可以存放在兩個地方,字串池(pool)和堆,編譯期確定如何給一個引用變數賦值

  • String s="abc";這種形式決定將從pool中尋找內容相同的字串並返回地址給s,pool中沒有就會在pool中新建並返回地址給s
  • String s = new String("abc");這種形式決定執行期將在堆上新建字串物件並返回給s,但這個物件不會加入到pool中
  • String s=s1+s2;s1和s2都是變數,這種形式決定將在堆上建立s1和s2(即便s1和s2指向的物件在池中已經存在,也會將值拷貝到物件建立新物件),然後建立s1+s2並賦給s
  • String s = "ab"+"cd";同1),都是來自於池
  • String s = "ab"+s1;類似3)
  • String s = S1+S2;S1和S2是常量,常量只能賦值一次,S1,S2如果在宣告的地方就賦值,那麼這個值在編譯期就是確定的,後面無法更改,S1+S2在執行前可確定S1/S2已經在池中存在,當然在池中進行,所以s指向pool;但是若S1,S2如果是例項常量在構造器中賦值,或是類常量在靜態塊中賦值,S1+S2無法確定二者皆來自於池,於是在堆上進行

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。