理解String 及 String.intern() 在實際中的應用

Java代碼 
 收藏代碼
  1. 1. 首先String不屬於8種基本數據類型,String是一個對象。   
  2.   
  3.   因爲對象的默認值是null,所以String的默認值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性。   
  4.   
  5.   2new String()和new String(“”)都是申明一個新的空字符串,是空串不是null;   
  6.   
  7.   3. String str=”kvill”;   
  8. String str=new String (“kvill”);的區別:   
  9.   
  10.   在這裏,我們不談堆,也不談棧,只先簡單引入常量池這個簡單的概念。   
  11.   
  12.   常量池(constant pool)指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。它包括了關於類、方法、接口等中的常量,也包括字符串常量。   
  13.   
  14.   看例1:   
  15.   
  16. String s0=”kvill”;   
  17. String s1=”kvill”;   
  18. String s2=”kv” + “ill”;   
  19. System.out.println( s0==s1 );   
  20. System.out.println( s0==s2 );    
  21.   
  22.   結果爲:   
  23.   
  24. true   
  25. true    
  26.   
  27.   首先,我們要知道Java會確保一個字符串常量只有一個拷貝。   
  28.   
  29.   因爲例子中的s0和s1中的”kvill”都是字符串常量,它們在編譯期就被確定了,所以s0==s1爲true;而”kv”和”ill”也都是字符串常量,當一個字符串由多個字符串常量連接而成時,它自己肯定也是字符串常量,所以s2也同樣在編譯期就被解析爲一個字符串常量,所以s2也是常量池中”kvill”的一個引用。   
  30.   
  31.   所以我們得出s0==s1==s2;   
  32.   
  33.   用new String() 創建的字符串不是常量,不能在編譯期就確定,所以new String() 創建的字符串不放入常量池中,它們有自己的地址空間。   
  34.   
  35.   看例2:   
  36.   
  37. String s0=”kvill”;   
  38. String s1=new String(”kvill”);   
  39. String s2=”kv” + new String(“ill”);   
  40. System.out.println( s0==s1 );   
  41. System.out.println( s0==s2 );   
  42. System.out.println( s1==s2 );    
  43.   
  44.   結果爲:   
  45.   
  46. false   
  47. false   
  48. false    
  49.   
  50.   例2中s0還是常量池中”kvill”的應用,s1因爲無法在編譯期確定,所以是運行時創建的新對象”kvill”的引用,s2因爲有後半部分new String(“ill”)所以也無法在編譯期確定,所以也是一個新創建對象”kvill”的應用;明白了這些也就知道爲何得出此結果了。   
  51.   
  52.   4. String.intern():   
  53.   
  54.   再補充介紹一點:存在於.class文件中的常量池,在運行期被JVM裝載,並且可以擴充。String的intern()方法就是擴充常量池的一個方法;當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字符串常量,如果有,則返回其的引用,如果沒有,則在常量池中增加一個Unicode等於str的字符串並返回它的引用;看例3就清楚了   
  55.   
  56.   例3:   
  57.   
  58. String s0= “kvill”;   
  59. String s1=new String(”kvill”);   
  60. String s2=new String(“kvill”);   
  61. System.out.println( s0==s1 );   
  62. System.out.println( “**********” );   
  63. s1.intern();   
  64. s2=s2.intern(); //把常量池中“kvill”的引用賦給s2   
  65. System.out.println( s0==s1);   
  66. System.out.println( s0==s1.intern() );   
  67. System.out.println( s0==s2 );    
  68.   
  69.   結果爲:   
  70.   
  71. false   
  72. **********   
  73. false //雖然執行了s1.intern(),但它的返回值沒有賦給s1   
  74. true //說明s1.intern()返回的是常量池中”kvill”的引用   
  75. true    
  76.   
  77.   最後我再破除一個錯誤的理解:   
  78.   
  79.   有人說,“使用String.intern()方法則可以將一個String類的保存到一個全局String表中,如果具有相同值的Unicode字符串已經在這個表中,那麼該方法返回表中已有字符串的地址,如果在表中沒有相同值的字符串,則將自己的地址註冊到表中“如果我把他說的這個全局的String表理解爲常量池的話,他的最後一句話,“如果在表中沒有相同值的字符串,則將自己的地址註冊到表中”是錯的:   
  80.   
  81.   看例4:   
  82.   
  83. String s1=new String("kvill");   
  84. String s2=s1.intern();   
  85. System.out.println( s1==s1.intern() );   
  86. System.out.println( s1+" "+s2 );   
  87. System.out.println( s2==s1.intern() );    
  88.   
  89.   結果:   
  90.   
  91. false   
  92. kvill kvill   
  93. true    
  94.   
  95.   在這個類中我們沒有聲名一個”kvill”常量,所以常量池中一開始是沒有”kvill”的,當我們調用s1.intern()後就在常量池中新添加了一個”kvill”常量,原來的不在常量池中的”kvill”仍然存在,也就不是“將自己的地址註冊到常量池中”了。   
  96.   
  97.   s1==s1.intern()爲false說明原來的“kvill”仍然存在;   
  98.   
  99.   s2現在爲常量池中“kvill”的地址,所以有s2==s1.intern()爲true。   
  100.   
  101.   5. 關於equals()和==:   
  102.   
  103.   這個對於String簡單來說就是比較兩字符串的Unicode序列是否相當,如果相等返回true;而==是比較兩字符串的地址是否相同,也就是是否是同一個字符串的引用。   
  104.   
  105.   6. 關於String是不可變的  
  106.   
  107.   這一說又要說很多,大家只要知道String的實例一旦生成就不會再改變了,比如說:String str=”kv”+”ill”+” “+”ans”;   
  108. 就是有4個字符串常量,首先”kv”和”ill”生成了”kvill”存在內存中,然後”kvill”又和” “ 生成 ”kvill “存在內存中,最後又和生成了”kvill ans”;並把這個字符串的地址賦給了str,就是因爲String的“不可變”產生了很多臨時變量,這也就是爲什麼建議用StringBuffer的原因了,因爲StringBuffer是可改變的  

 出處:http://www.iteye.com/topic/122206

 

By the way,關於 String.intern() 在實際中的應用,我在tomcat的源碼中找到了一個地方用到了,如下:

 

Java代碼 
 收藏代碼
  1. /* 
  2.  * Copyright 1999,2004-2005 The Apache Software Foundation. 
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  *  
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  * ==================================================================== 
  16.  * 
  17.  * This software consists of voluntary contributions made by many 
  18.  * individuals on behalf of the Apache Software Foundation and was 
  19.  * originally based on software copyright (c) 1999, International 
  20.  * Business Machines, Inc., http://www.apache.org.  For more 
  21.  * information on the Apache Software Foundation, please see 
  22.  * <http://www.apache.org/>. 
  23.  */  
  24.   
  25. package org.apache.jasper.xmlparser;  
  26.   
  27. /** 
  28.  * This class is a symbol table implementation that guarantees that 
  29.  * strings used as identifiers are unique references. Multiple calls 
  30.  * to <code>addSymbol</code> will always return the same string 
  31.  * reference. 
  32.  * <p> 
  33.  * The symbol table performs the same task as <code>String.intern()</code> 
  34.  * with the following differences: 
  35.  * <ul> 
  36.  *  <li> 
  37.  *   A new string object does not need to be created in order to 
  38.  *   retrieve a unique reference. Symbols can be added by using 
  39.  *   a series of characters in a character array. 
  40.  *  </li> 
  41.  *  <li> 
  42.  *   Users of the symbol table can provide their own symbol hashing 
  43.  *   implementation. For example, a simple string hashing algorithm 
  44.  *   may fail to produce a balanced set of hashcodes for symbols 
  45.  *   that are <em>mostly</em> unique. Strings with similar leading 
  46.  *   characters are especially prone to this poor hashing behavior. 
  47.  *  </li> 
  48.  * </ul> 
  49.  * 
  50.  * @author Andy Clark 
  51.  * @version $Id: SymbolTable.java 306179 2005-07-27 15:12:04Z yoavs $ 
  52.  */  
  53. public class SymbolTable {  
  54.   
  55.     //  
  56.     // Constants  
  57.     //  
  58.   
  59.     /** Default table size. */  
  60.     protected static final int TABLE_SIZE = 101;  
  61.   
  62.     //  
  63.     // Data  
  64.     //  
  65.   
  66.     /** Buckets. */  
  67.     protected Entry[] fBuckets = null;  
  68.   
  69.     // actual table size  
  70.     protected int fTableSize;  
  71.   
  72.     //  
  73.     // Constructors  
  74.     //  
  75.   
  76.     /** Constructs a symbol table with a default number of buckets. */  
  77.     public SymbolTable() {  
  78.         this(TABLE_SIZE);  
  79.     }  
  80.   
  81.     /** Constructs a symbol table with a specified number of buckets. */  
  82.     public SymbolTable(int tableSize) {  
  83.         fTableSize = tableSize;  
  84.         fBuckets = new Entry[fTableSize];  
  85.     }  
  86.   
  87.     //  
  88.     // Public methods  
  89.     //  
  90.   
  91.     /** 
  92.      * Adds the specified symbol to the symbol table and returns a 
  93.      * reference to the unique symbol. If the symbol already exists, 
  94.      * the previous symbol reference is returned instead, in order 
  95.      * guarantee that symbol references remain unique. 
  96.      * 
  97.      * @param symbol The new symbol. 
  98.      */  
  99.     public String addSymbol(String symbol) {  
  100.   
  101.         // search for identical symbol  
  102.         int bucket = hash(symbol) % fTableSize;  
  103.         int length = symbol.length();  
  104.         OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {  
  105.             if (length == entry.characters.length) {  
  106.                 for (int i = 0; i < length; i++) {  
  107.                     if (symbol.charAt(i) != entry.characters[i]) {  
  108.                         continue OUTER;  
  109.                     }  
  110.                 }  
  111.                 return entry.symbol;  
  112.             }  
  113.         }  
  114.   
  115.         // create new entry  
  116.         Entry entry = new Entry(symbol, fBuckets[bucket]);  
  117.         fBuckets[bucket] = entry;  
  118.         return entry.symbol;  
  119.   
  120.     } // addSymbol(String):String  
  121.   
  122.     /** 
  123.      * Adds the specified symbol to the symbol table and returns a 
  124.      * reference to the unique symbol. If the symbol already exists, 
  125.      * the previous symbol reference is returned instead, in order 
  126.      * guarantee that symbol references remain unique. 
  127.      * 
  128.      * @param buffer The buffer containing the new symbol. 
  129.      * @param offset The offset into the buffer of the new symbol. 
  130.      * @param length The length of the new symbol in the buffer. 
  131.      */  
  132.     public String addSymbol(char[] buffer, int offset, int length) {  
  133.   
  134.         // search for identical symbol  
  135.         int bucket = hash(buffer, offset, length) % fTableSize;  
  136.         OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {  
  137.             if (length == entry.characters.length) {  
  138.                 for (int i = 0; i < length; i++) {  
  139.                     if (buffer[offset + i] != entry.characters[i]) {  
  140.                         continue OUTER;  
  141.                     }  
  142.                 }  
  143.                 return entry.symbol;  
  144.             }  
  145.         }  
  146.   
  147.         // add new entry  
  148.         Entry entry = new Entry(buffer, offset, length, fBuckets[bucket]);  
  149.         fBuckets[bucket] = entry;  
  150.         return entry.symbol;  
  151.   
  152.     } // addSymbol(char[],int,int):String  
  153.   
  154.     /** 
  155.      * Returns a hashcode value for the specified symbol. The value 
  156.      * returned by this method must be identical to the value returned 
  157.      * by the <code>hash(char[],int,int)</code> method when called 
  158.      * with the character array that comprises the symbol string. 
  159.      * 
  160.      * @param symbol The symbol to hash. 
  161.      */  
  162.     public int hash(String symbol) {  
  163.   
  164.         int code = 0;  
  165.         int length = symbol.length();  
  166.         for (int i = 0; i < length; i++) {  
  167.             code = code * 37 + symbol.charAt(i);  
  168.         }  
  169.         return code & 0x7FFFFFF;  
  170.   
  171.     } // hash(String):int  
  172.   
  173.     /** 
  174.      * Returns a hashcode value for the specified symbol information. 
  175.      * The value returned by this method must be identical to the value 
  176.      * returned by the <code>hash(String)</code> method when called 
  177.      * with the string object created from the symbol information. 
  178.      * 
  179.      * @param buffer The character buffer containing the symbol. 
  180.      * @param offset The offset into the character buffer of the start 
  181.      *               of the symbol. 
  182.      * @param length The length of the symbol. 
  183.      */  
  184.     public int hash(char[] buffer, int offset, int length) {  
  185.   
  186.         int code = 0;  
  187.         for (int i = 0; i < length; i++) {  
  188.             code = code * 37 + buffer[offset + i];  
  189.         }  
  190.         return code & 0x7FFFFFF;  
  191.   
  192.     } // hash(char[],int,int):int  
  193.   
  194.     /** 
  195.      * Returns true if the symbol table already contains the specified 
  196.      * symbol. 
  197.      * 
  198.      * @param symbol The symbol to look for. 
  199.      */  
  200.     public boolean containsSymbol(String symbol) {  
  201.   
  202.         // search for identical symbol  
  203.         int bucket = hash(symbol) % fTableSize;  
  204.         int length = symbol.length();  
  205.         OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {  
  206.             if (length == entry.characters.length) {  
  207.                 for (int i = 0; i < length; i++) {  
  208.                     if (symbol.charAt(i) != entry.characters[i]) {  
  209.                         continue OUTER;  
  210.                     }  
  211.                 }  
  212.                 return true;  
  213.             }  
  214.         }  
  215.   
  216.         return false;  
  217.   
  218.     } // containsSymbol(String):boolean  
  219.   
  220.     /** 
  221.      * Returns true if the symbol table already contains the specified 
  222.      * symbol. 
  223.      * 
  224.      * @param buffer The buffer containing the symbol to look for. 
  225.      * @param offset The offset into the buffer. 
  226.      * @param length The length of the symbol in the buffer. 
  227.      */  
  228.     public boolean containsSymbol(char[] buffer, int offset, int length) {  
  229.   
  230.         // search for identical symbol  
  231.         int bucket = hash(buffer, offset, length) % fTableSize;  
  232.         OUTER: for (Entry entry = fBuckets[bucket]; entry != null; entry = entry.next) {  
  233.             if (length == entry.characters.length) {  
  234.                 for (int i = 0; i < length; i++) {  
  235.                     if (buffer[offset + i] != entry.characters[i]) {  
  236.                         continue OUTER;  
  237.                     }  
  238.                 }  
  239.                 return true;  
  240.             }  
  241.         }  
  242.   
  243.         return false;  
  244.   
  245.     } // containsSymbol(char[],int,int):boolean  
  246.   
  247.     //  
  248.     // Classes  
  249.     //  
  250.   
  251.     /** 
  252.      * This class is a symbol table entry. Each entry acts as a node 
  253.      * in a linked list. 
  254.      */  
  255.     protected static final class Entry {  
  256.   
  257.         //  
  258.         // Data  
  259.         //  
  260.   
  261.         /** Symbol. */  
  262.         public String symbol;  
  263.   
  264.         /** 
  265.          * Symbol characters. This information is duplicated here for 
  266.          * comparison performance. 
  267.          */  
  268.         public char[] characters;  
  269.   
  270.         /** The next entry. */  
  271.         public Entry next;  
  272.   
  273.         //  
  274.         // Constructors  
  275.         //  
  276.   
  277.         /** 
  278.          * Constructs a new entry from the specified symbol and next entry 
  279.          * reference. 
  280.          */  
  281.         public Entry(String symbol, Entry next) {  
  282.             this.symbol = symbol.intern();  
  283.             characters = new char[symbol.length()];  
  284.             symbol.getChars(0, characters.length, characters, 0);  
  285.             this.next = next;  
  286.         }  
  287.   
  288.         /** 
  289.          * Constructs a new entry from the specified symbol information and 
  290.          * next entry reference. 
  291.          */  
  292.         public Entry(char[] ch, int offset, int length, Entry next) {  
  293.             characters = new char[length];  
  294.             System.arraycopy(ch, offset, characters, 0, length);  
  295.             symbol = new String(characters).intern();  
  296.             this.next = next;  
  297.         }  
  298.   
  299.     } // class Entry  
  300.   
  301. // class SymbolTable  

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章