<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-199  評論-203  文章-11  trackbacks-0
    String類是Java中很重要的一個類,在此總結一下這個類的特別之處。下面的相關資料翻譯自《java語言規范》(第三版)和《java虛擬機規范》(第二版),有的直接摘引了原文。下面的代碼都是用SUN jdk1.6 javac來編譯。



    1.String literal,這里將它翻譯為字面常量,它由雙引號包圍的0個或多個字符組成,比如"abc","Hello World"等等。一個String字面常量總是引用相同的String實例,比如"abc","abc"兩個常量引用的是同一個對象。



    程序測試:

    package testPackage;

    class Test {

            public static void main(String[] args) {

                    String hello = "Hello", lo = "lo";

                    System.out.print((hello == "Hello") + " ");

                    System.out.print((Other.hello == hello) + " ");

                    System.out.print((other.Other.hello == hello) + " ");

                    System.out.print((hello == ("Hel"+"lo")) + " ");

                    System.out.print((hello == ("Hel"+lo)) + " ");

                    System.out.println(hello == ("Hel"+lo).intern());

            }

    }



    class Other { static String hello = "Hello"; }



    另一個包:



    package other;

    public class Other { static String hello = "Hello"; }

    輸出:

    true true true true false true

    結論有六點:

    1)  同一個包下,同一個類中的相同的String字面常量表示對同一個String對象的引用。

    2)  同一個包下,不同的類中的相同的String字面常量表示對同一個String對象的引用。

    3)  不同包下,不同類中的相同String字面常量同樣表示對同一個String對象的引用。

    4)  通過常量表達式計算的String,計算在編譯時進行,并將它作為String字面常量對待。

    5)  通過連接操作得到的String(非常量表達式),連接操作是運行時進行的,會新創建對象,所以它們是不同的。

    6)  顯式的對一個計算得到的String調用intern操作,得到的結果是已經存在的相同內容的String字面常量。

    補充說明:

    1)像這樣的問題,String str = "a"+"b"+"c"+"d";

    運行這條語句會產生幾個String對象?1個。參考上面第5條,通過常量表達式得到的String 是編譯時計算的,因此執行這句話時只有"abcd"著一個String對象存在。

    常量表達是的定義可以參考java語言規范。另例:

          final String str1 = "a";

          String str2 = str1+"b";

    執行第二句話會有幾個String對象產生?1個。因為str1是常量,所以str1+"b"也是常量表達式,在編譯時計算。

        遇到這種問題時,不要說它依賴于具體的編譯器或者虛擬機實現,因為這就是規范里有的。一般的說,java的編譯器實現應該遵守《java語言規范》,而java虛擬機實現應該遵守《java虛擬機規范》。



    2)不要這樣使用字符串:

    String str = new String("abc");

        參考文檔中的說明:

    String

    public String(String original)

        初始化一個新創建的 String 對象,使其表示一個與參數相同的字符序列;換句話說,新創建的字符串是該參數字符串的副本。由于 String 是不可變的,所以無需使用此構造方法,除非需要 original 的顯式副本。

    參數:

    original - 一個 String。

    注意:無需使用此構造方法!!!



    3)單獨的說明第6點:

    String str = new String("abc");

    str = str.intern();

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

        很明顯,在這個例子中"abc"引用的對象已經在字符串池中了,再調用intern返回的是已經存在池中內容為"abc"的字符換對象的引用。在上面的例子中也說明了這個問題。

    2. String類的實例表示表示Unicode字符序列。String字面常量是指向String實例的引用。(字面常量是“引用”!)

    3.String轉換

        對于基本類型先轉換為引用類型;引用類型調用toString()方法得到String,如果該引用類型為null,轉換得到的字符串為"null"。

    4. String鏈接操作“+”

        如果“+”操作的結果不是編譯期常量,將會隱式創建一個新的對象。為了提高性能,具體的實現可以采用StringBuffer,StringBuilder類對多個部分進行連接,最后再轉換為String,從而避免生成再丟棄中間的String對象。為了達到共享實例的目的,編譯期常量總是“interned”的。

    例子:

    String a = "hello ";

    String b = a+1+2+"world!";

    反匯編結果:

    0:  ldc #2; //String hello

      2:  astore_1

      3:  new #3; //class java/lang/StringBuilder

      6:  dup

      7:  invokespecial #4; //Method java/lang/StringBuilder." <init>":()V

      10: aload_1

      11: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      14: iconst_1

      15: invokevirtual #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

      18: iconst_2

      19: invokevirtual #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

      22: ldc #7; //String world!

      24: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      27: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

      30: astore_2



    實際就是

    String b = new StringBuilder().append(a).append(1).append(2).append("world").toString();

    這里就使用StringBuilder來避免中間臨時String對象的產生而導致性能下降。

        補充例子,下面的兩個例子主要是對編譯時常量做一個說明:

    1)

    String c = "c";

    String str = "a"+"b"+c;



    2)

    String c = "c";

    String str = c+"a"+"b";

    1)中,str="a"+"b"+c;編譯器分析是會把"a"+"b"作為編譯時常量,生成字面常量"ab",所以實際執行這句話時,鏈接的是"ab"和c。實際相當于執行了

    String str = new StringBuilder().append("ab").append(c).toString();

    2)中,String str = c+"a"+"b";

    編譯器分析到c為變量,后面的"a"+"b"就不會作為編譯時常量來運算了。

    實際運行時相當于執行

    String str = new StringBuilder().append(c).append("a").append("b").toString();

    5.String對象的創建:

    1)  包含String字面常量的類或者接口在加載時創建表示該字面常量的String對象。以下兩種情況下不會創建新String對象。

    a)  一個相同的字面常量已經出現過。

    b)  一個相同內容的字符串已經調用了intern操作(比如經過運算產生的字符串調用intern的情形)。

    2)  非常量表達式的字符串連接操作有時會產生表示結果的String對象。

    3)  String字面常量來自類或接口的二進制表示中(也就是class文件中)的CONSTANT_String_info 結構。CONSTANT_String_info結構給出了構成字符串字面常量的Unicode字符序列。

    4)  為了生成字符串字面常量,java虛擬機檢查 CONSTANT_String_info結構給出的字符序列:

    a)  如果與CONSTANT_String_info結構中給出的字符換內容相同的串實例已經調用過String.intern,得到的字符串字面常量就來自該串的同一實例。

    b)  否則,根據CONSTANT_String_info 中的字符序列創建一個新的字符串實例,然后調用intern方法。

    例子:一個SCJP題目

    11. public String makinStrings() {
    12. String s = “Fred”;
    13. s = s + “47”;
    14. s = s.substring(2, 5);
    15. s = s.toUpperCase();
    16. return s.toString();
    17. }
    How many String objects will be created when this method is invoked?

    答案是3個。上面已經說明,"Fred","47"是字符串字面常量,它們在在類加載時創建的。這里題目問,方法調用時(!)有多少個String對象被創建,兩個字面常量自然不包括在內。3個是:"Fred47","ed4","ED4"。

    6.String與基本類型的包裝類比較

        相同點,它們都是不變類,使用"=="判斷時可能會有類似的性質。

        在java 5之后,java增加了自動裝箱和拆箱功能。因此,就有了這樣的性質:

    Integer i = 5;

    Integer j = 5;

    System.out.println(i == j);

    結果:true.

      這表面上看來是和String相同點,但其實現是極為不同的。這里作為一個不同點來介紹。

        眾所周知,自動裝箱是這樣實現的:

    Integer i = 5;

    相當于

    Integer i = Integer.valueOf(5);//注意不是new Integer(5),這就無法滿足java語言規范中的約定了,約定見本文最后

        而在Integer中,靜態的創建了表示從-128~+127之間數據的Integer對象,這個范圍之內的數進行裝箱操作,只要返回相應的對象即可。因此

    Integer i = 5;

    Integer j = 5;

    我們得到的是同一個對象。這是通過類庫的設計來實現的。而String的共享是通過java虛擬機的直接支持來實現的,這是它們本質的不同。

        這是Integer類中的部分代碼:

    private static class IntegerCache {

      private IntegerCache(){}

      static final Integer cache[] = new Integer[-(-128) + 127 + 1];

      static {

          for(int i = 0; i < cache.length; i++)

          cache[i] = new Integer(i - 128);

      }

    }

    public static Integer valueOf(int i) {

      final int offset = 128;

      if (i >= -128 && i <= 127) { // must cache

          return IntegerCache.cache[i + offset];

      }

        return new Integer(i);

        }

    關于基本類型的裝箱,Java語言規范中有如下說明:

        如果被裝箱的變量p為true,false,一個處于\u0000~\u007f之間的byte/char,或一個處于-128~+127之間的int/short,令r1和r2為對p的任何兩個裝箱操作的結果,則r1==r2總是成立的。理想的情況下,對一個基本類型變量執行裝箱操作,應該總是得到一個相同的引用。但在實踐中,在現存的技術條件下,這是不現實的。上面的規則是一個注重實效的折衷。

        最后一點,要理解java的方法調用時的傳參模型:java中只有pass by value。(不明確這一點,就有亂七八糟的解釋,比如典型的Java既有傳值,又有傳引用,String很特殊……)

    //改變參數的值?

    public void test(String str){

        str = "Hello";

    }

    //改變參數的值?

    public void test(StringBuffer buffer){

        buffer = new StringBuffer("Hello");

    }

    //交換兩個Integer?

    public void swap(Integer a,Integer b){

        Integer temp = a;

        a = b;

        b = temp;

    }

    這三個方法全是沒有意義的方法。
    Original text: http://topic.csdn.net/u/20090519/18/7b8cf7ef-bc06-4d26-8a2c-692eb0562231.html
    posted on 2009-05-21 08:02 Werther 閱讀(273) 評論(0)  編輯  收藏 所屬分類: 10.Java
    主站蜘蛛池模板: 精品少妇人妻AV免费久久洗澡| 亚洲AV无码不卡在线播放| 亚洲男人的天堂在线va拉文| 国产亚洲3p无码一区二区| 亚洲国产夜色在线观看| 香港特级三A毛片免费观看| 任你躁在线精品免费| 色视频色露露永久免费观看 | 一个人看的免费视频www在线高清动漫 | 精品成人免费自拍视频| 成人毛片免费视频| 亚洲综合网站色欲色欲| 中文字幕在线日亚洲9| 成人精品一区二区三区不卡免费看| 精品久久久久成人码免费动漫| 精品亚洲视频在线观看 | 日本永久免费a∨在线视频| 99热这里只有精品免费播放| 国产三级电影免费观看| 亚洲国语在线视频手机在线| 皇色在线免费视频| 日韩免费观看的一级毛片| 亚洲AV成人无码久久精品老人| 国产亚洲精彩视频| 中文字幕影片免费在线观看 | 国产香蕉九九久久精品免费| 国产亚洲精品成人AA片新蒲金| 亚洲AV综合色区无码二区偷拍 | 99re这里有免费视频精品| 红杏亚洲影院一区二区三区| 日本系列1页亚洲系列| 性色av免费观看| 亚洲成a人片在线网站| 日本免费高清视频| 久久国产成人亚洲精品影院 | 亚洲成AV人影片在线观看| 精品久久8x国产免费观看| 亚洲AV成人片色在线观看| a级片在线免费看| 国产亚洲精久久久久久无码AV| 美女无遮挡免费视频网站|