java中的String設計原理

首先,必須強調一點:String Pool不是在堆區,也不是在棧區,而是存在於方法區(Method Area)java

解析:程序員

String Pool是常量池(Constant  Pool)中的一塊。數組

咱們知道,常量就是不能夠再改變的值,給它建一個池子很明顯是爲了加快程序運行的速度;在一個程序中,常量和變量是相對存在的;變量由於可變性因此通常存在於棧中,而常量去做爲一個特殊羣體被存在在常量池中。安全

常量池(constant pool)指的是在編譯期被肯定,並被保存在已編譯的.class文件中的一些數據。--- (很明顯在方法區)數據結構

它包括了關於類、方法、接口等中的常量,也包括字符串常量(這個就是Sring Pool啦)。jvm

在編譯好的class文件中,有個區域稱爲Constant Pool,它是一個由數組組成的表,類型爲cp_info constant_pool[],用來存儲程序中使用的各類常量,包括Class/String/Integer等各類基本Java數據類型。性能

=======  ================ui

上面這些,簡單理解:一個Class類,它裏面有常量的存在,好比 int a=10;String b="123450";它們在JVM看來就是常量(固然在方法中可能被修改啦),在Class被加載時,JVM特地都把它放在一個數組中維護起來,而且把該數組放在方法區中,起名叫常量池。spa

 

常量池存在於方法區,它包含各類類型的常量(8個基本數據類型,包裝類型等)線程

咱們把常量池中的String Pool中的常量做爲對象來看待 --- 由於String就是對象,String類型的常量天然也是對象啦!

好比:

String str1 = new String("Hello");

它建立了2個對象,一個是堆中的String對象,一個是String Pool中的String對象。

 

要理解Java中String的運做方式,必須明確一點:String是一個非可變類(immutable)。什麼是非可變類呢?簡單說來,非可變類的實例是不能被修改的,每一個實例中包含的信息都必須在該實例建立的時候就提供出來,而且在對象的整個生存週期內固定不變。Java爲何要把String設計爲非可變類呢?你能夠問問 james Gosling :)。可是非可變類確實有着自身的優點,如狀態單一,對象簡單,便於維護。其次,該類對象對象本質上是線程安全的,不要求同步。此外用戶能夠共享非可變對象,甚至能夠共享它們的內部信息。(詳見 《Effective java》item 13)。String類在java中被大量運用,甚至在class文件中都有其身影,所以將其設計爲簡單輕便的非可變類是比較合適的。

1、建立。
    好了,知道String是非可變類之後,咱們能夠進一步瞭解String的構造方式了。建立一個Stirng對象,主要就有如下兩種方式:

java 代碼
  1. String str1 = new String("abc");    
  2. Stirng str2 = "abc";  

     雖然兩個語句都是返回一個String對象的引用,可是jvm對二者的處理方式是不同的。對於第一種,jvm會立刻在heap中建立一個String對象,而後將該對象的引用返回給用戶。對於第二種,jvm首先會在內部維護的String Pool中經過String的 equals 方法查找是對象池中是否存放有該String對象,若是有,則返回已有的String對象給用戶,而不會在heap中從新建立一個新的String對象;若是對象池中沒有該String對象,jvm則在heap中建立新的String對象,將其引用返回給用戶,同時將該引用添加至String Pool中。注意:使用第一種方法建立對象時,jvm是不會主動把該對象放到String Pool
裏面的,除非程序調用 String的intern方法。看下面的例子:

java 代碼
  1. String str1 = new String("abc"); //jvm 在堆上建立一個String對象   
  2.   
  3.  //jvm 在strings pool中找不到值爲「abc」的字符串,所以   
  4.  //在堆上建立一個String對象,並將該對象的引用加入至strings pool中   
  5.  //此時堆上有兩個String對象   
  6. Stirng str2 = "abc";   
  7.   
  8.  if(str1 == str2){   
  9.          System.out.println("str1 == str2");   
  10.  }else{   
  11.          System.out.println("str1 != str2");   
  12.  }   
  13.   //打印結果是 str1 != str2,由於它們是堆上兩個不一樣的對象   
  14.   
  15.   String str3 = "abc";   
  16.  //此時,jvm發現String Pool中已有「abc」對象了,由於「abc」equals 「abc」   
  17.  //所以直接返回str2指向的對象給str3,也就是說str2和str3是指向同一個對象的引用   
  18.   if(str2 == str3){   
  19.          System.out.println("str2 == str3");   
  20.   }else{   
  21.          System.out.println("str2 != str3");   
  22.   }   
  23.  //打印結果爲 str2 == str3  

   再看下面的例子:

java 代碼
  1. String str1 = new String("abc"); //jvm 在堆上建立一個String對象   
  2.   
  3. str1 = str1.intern();   
  4. //程序顯式將str1放到String Pool中,intern運行過程是這樣的:首先查看String Pool   
  5. //有沒「abc」對象的引用,沒有,則在堆中新建一個對象,而後將新對象的引用加入至   
  6. //String Pool中。執行完該語句後,str1原來指向的String對象已經成爲垃圾對象了,隨時會   
  7. //被GC收集。   
  8.   
  9. //此時,jvm發現String Pool中已有「abc」對象了,由於「abc」equals 「abc」   
  10. //所以直接返回str1指向的對象給str2,也就是說str2和str1引用着同一個對象,   
  11. //此時,堆上的有效對象只有一個。   
  12. Stirng str2 = "abc";   
  13.   
  14.  if(str1 == str2){   
  15.          System.out.println("str1 == str2");   
  16.  }else{   
  17.          System.out.println("str1 != str2");   
  18.  }   
  19.   //打印結果是 str1 == str2   
  20.   

 

    爲何jvm能夠這樣處理String對象呢?就是由於String的非可變性。既然所引用的對象一旦建立就永不更改,那麼多個引用共用一個對象時互不影響。


2、串接(Concatenation)。
     java程序員應該都知道濫用String的串接操做符是會影響程序的性能的。性能問題從何而來呢?歸根結底就是String類的非可變性。既然String對象都是非可變的,也就是對象一旦建立了就不可以改變其內在狀態了,可是串接操做明顯是要增加字符串的,也就是要改變String的內部狀態,二者出現了矛盾。怎麼辦呢?要維護String的非可變性,只好在串接完成後新建一個String 對象來表示新產生的字符串了。也就是說,每一次執行串接操做都會致使新對象的產生,若是串接操做執行很頻繁,就會致使大量對象的建立,性能問題也就隨之而來了。
    爲了解決這個問題,jdk爲String類提供了一個可變的配套類,StringBuffer。使用StringBuffer對象,因爲該類是可變的,串接時僅僅時改變了內部數據結構,而不會建立新的對象,所以性能上有很大的提升。針對單線程,jdk 5.0還提供了StringBuilder類,在單線程環境下,因爲不用考慮同步問題,使用該類使性能獲得進一步的提升。

3、String的長度   咱們可使用串接操做符獲得一個長度更長的字符串,那麼,String對象最多能容納多少字符呢?查看String的源代碼咱們能夠得知類String中是使用域 count 來記錄對象字符的數量,而count 的類型爲 int,所以,咱們能夠推測最長的長度爲 2^32,也就是4G。    不過,咱們在編寫源代碼的時候,若是使用 Sting str = "aaaa";的形式定義一個字符串,那麼雙引號裏面的ASCII字符最多隻能有 65534 個。爲何呢?由於在class文件的規範中, CONSTANT_Utf8_info表中使用一個16位的無符號整數來記錄字符串的長度的,最多能表示 65536個字節,而java class 文件是使用一種變體UTF-8格式來存放字符的,null值使用兩個字節來表示,所以只剩下 65536- 2 = 65534個字節。也正是變體UTF-8的緣由,若是字符串中含有中文等非ASCII字符,那麼雙引號中字符的數量會更少(一箇中文字符佔用三個字節)。若是超出這個數量,在編譯的時候編譯器會報錯。

相關文章
相關標籤/搜索