成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入研究Java String

番茄西紅柿 / 1573人閱讀

摘要:所以我決定先從類入手,深入的研究一番來開個好頭。之所以會有以上的效果,是因為有字符串常量池的存在。同時運行時實例創(chuàng)建的全局字符串常量池中有一個表,總是為池中的每個字符串對象維護一個引用,所以這些對象不會被。

開始寫 Java 一年來,一直都是遇到什么問題再去解決,還沒有主動的深入的去學習過 Java 語言的特性和深入閱讀 JDK 的源碼。既然決定今后靠 Java
吃飯,還是得花些心思在上面,放棄一些打游戲的時間,系統(tǒng)深入的去學習。

Java String 是 Java 編程中最常用的類之一,也是 JDK 提供的最基礎(chǔ)的類。所以我決定先從 String 類入手,深入的研究一番來開個好頭。

類定義與類成員

打開 JDK 中的 String 源碼,最先應(yīng)當關(guān)注 String 類的定義。

public final class String
    implements java.io.Serializable, Comparable, CharSequence
不可繼承與不可變

寫過 Java 的人都知道, 當 final 關(guān)鍵字修飾類時,代表此類不可繼承。所以 String 類是不能被外部繼承。這時候我們可能會好奇,String 的設(shè)計者
為什么要把它設(shè)計成不可繼承的呢。我在知乎上找到了相關(guān)的問題和討論,
我覺得首位的回答已經(jīng)說的很明白了。String 做為 Java 的最基礎(chǔ)的引用數(shù)據(jù)類型,最重要的一點就是不可變性,所以使用 final 就是為了**禁止繼承
破壞了 String 的不可變的性質(zhì)**。

實現(xiàn)類的不可變性,不光是用 final 修飾類這么簡單,從源碼中可以看到,String 實際上是對一個字符數(shù)組的封裝,而字符數(shù)組是私有的,并且沒有提供
任何可以修改字符數(shù)組的方法,所以一旦初始化完成, String 對象便無法被修改。

序列化

從上面的類定義中我們看到了 String 實現(xiàn)了序列化的接口 Serializable,所以 String 是支持序列化和反序列化的。
什么是Java對象的序列化?相信很多和我一樣的 Java 菜鳥都有這樣疑問。深入分析Java的序列化與反序列化這篇文章中的這一段話
解釋的很好。

Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下,
只有當JVM處于運行時,這些對象才可能存在,
即,這些對象的生命周期不會比JVM的生命周期更長。但在現(xiàn)實應(yīng)用中,
就可能要求在JVM停止運行之后能夠保存(持久化)指定的對象,并在將來重新讀取被保存的對象。
Java對象序列化就能夠幫助我們實現(xiàn)該功能。
使用Java對象序列化,在保存對象時,會把其狀態(tài)保存為一組字節(jié),在未來,再將這些字節(jié)組裝成對象。
必須注意地是,對象序列化保存的是對象的”狀態(tài)”,即它的成員變量。由此可知,對象序列化不會關(guān)注類中的靜態(tài)變量。
除了在持久化對象時會用到對象序列化之外,當使用RMI(遠程方法調(diào)用),或在網(wǎng)絡(luò)中傳遞對象時,都會用到對象序列化。
Java序列化API為處理對象序列化提供了一個標準機制,該API簡單易用。

在 String 源碼中,我們也可以看到支持序列化的類成員定義。

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    /**
     * Class String is special cased within the Serialization Stream Protocol.
     *
     * A String instance is written into an ObjectOutputStream according to
     * 
     * Object Serialization Specification, Section 6.2, "Stream Elements"
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

serialVersionUID 是一個序列化版本號,Java 通過這個 UID 來判定反序列化時的字節(jié)流與本地類的一致性,如果相同則認為一致,
可以進行反序列化,如果不同就會拋出異常。

serialPersistentFields 這個定義則比上一個少見許多,大概猜到是與序列化時的類成員有關(guān)系。為了弄懂這個字段的意義,我 google 百度齊上,也
僅僅只找到了 JDK 文檔對類 ObjectStreamField的一丁點描述, `A description of a Serializable field from a Serializable class.
An array of ObjectStreamFields is used to declare the Serializable fields of a class.` 大意是這個類用來描述序列化類的一個序列化字段,
如果定義一個此類的數(shù)組則可以聲明類需要被序列化的字段。但是還是沒有找到這個類的具體用法和作用是怎樣的。后來我仔細看了一下這個字段的定義,
serialVersionUID 應(yīng)該是同樣通過具體字段名來定義各種規(guī)則的,然后我直接搜索了關(guān)鍵字 serialPersistentFields,終于找到了它的具體作用。
即,**默認序列化自定義包括關(guān)鍵字 transient 和靜態(tài)字段名 serialPersistentFields,transient 用于指定哪個字段不被默認序列化,
serialPersistentFields 用于指定哪些字段需要被默認序列化。如果同時定義了 serialPersistentFieldstransienttransient 會被忽略。**
我自己也測試了一下,確實是這個效果。

知道了 serialPersistentFields 的作用以后,問題又來了,既然這個靜態(tài)字段是用來定義參與序列化的類成員的,那為什么在 String 中這個數(shù)組的長度定義為0?
經(jīng)過一番搜索查找資料以后,還是沒有找到一個明確的解釋,期待如果有大佬看到能解答一下。

可排序

String 類還實現(xiàn)了 Comparable 接口,Comparable接口只有一個方法 public int compareTo(T o),實現(xiàn)了這個接口就意味著該類支持排序,
即可用 Collections.sortArrays.sort 等方法對該類的對象列表或數(shù)組進行排序。

在 String 中我們還可以看到這樣一個靜態(tài)變量,

 public static final Comparator CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
            implements Comparator, java.io.Serializable {
        // use serialVersionUID from JDK 1.2.2 for interoperability
        private static final long serialVersionUID = 8575799808933029326L;

        public int compare(String s1, String s2) {
            int n1 = s1.length();
            int n2 = s2.length();
            int min = Math.min(n1, n2);
            for (int i = 0; i < min; i++) {
                char c1 = s1.charAt(i);
                char c2 = s2.charAt(i);
                if (c1 != c2) {
                    c1 = Character.toUpperCase(c1);
                    c2 = Character.toUpperCase(c2);
                    if (c1 != c2) {
                        c1 = Character.toLowerCase(c1);
                        c2 = Character.toLowerCase(c2);
                        if (c1 != c2) {
                            // No overflow because of numeric promotion
                            return c1 - c2;
                        }
                    }
                }
            }
            return n1 - n2;
        }

        /** Replaces the de-serialized object. */
        private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
    }

從上面的源碼中可以看出,這個靜態(tài)成員是一個實現(xiàn)了 Comparator 接口的類的實例,而實現(xiàn)這個類的作用是比較兩個忽略大小寫的 String 的大小。

那么 ComparableComparator 有什么區(qū)別和聯(lián)系呢?同時 String 又為什么要兩個都實現(xiàn)一遍呢?

第一個問題這里就不展開了,總結(jié)一下就是,Comparable 是類的內(nèi)部實現(xiàn),一個類能且只能實現(xiàn)一次,而 Comparator 則是外部實現(xiàn),可以通過不改變
類本身的情況下,為類增加更多的排序功能。
所以我們也可以為 String 實現(xiàn)一個 Comparator使用,具體可以參考Comparable與Comparator的區(qū)別這篇文章。

String 實現(xiàn)了兩種比較方法的意圖,實際上是一目了然的。實現(xiàn) Comparable 接口為類提供了標準的排序方案,同時為了滿足大多數(shù)排序需求的忽略大小寫排序的情況,
String 再提供一個 Comparator 到公共靜態(tài)類成員中。如果還有其他的需求,那就只能我們自己實現(xiàn)了。

類方法

String 的方法大致可以分為以下幾類。

構(gòu)造方法

功能方法

工廠方法

intern方法

關(guān)于 String 的方法的解析,這篇文章已經(jīng)解析的夠好了,所以我這里也不再重復(fù)的說一遍了。不過
最后的 intern 方法值得我們?nèi)パ芯俊?/p> intern方法 字符串常量池

String 做為 Java 的基礎(chǔ)類型之一,可以使用字面量的形式去創(chuàng)建對象,例如 String s = "hello"。當然也可以使用 new 去創(chuàng)建 String 的對象,
但是幾乎很少看到這樣的寫法,久而久之我便習慣了第一種寫法,但是卻不知道背后大有學問。下面一段代碼可以看出他們的區(qū)別。

public class StringConstPool {
    public static void main(String[] args) {
        String s1 = "hello world";
        String s2 = new String("hello world");
        String s3 = "hello world";
        String s4 = new String("hello world");
        String s5 = "hello " + "world";
        String s6 = "hel" + "lo world";
        String s7 = "hello";
        String s8 = s7 + " world";
        
        System.out.println("s1 == s2: " + String.valueOf(s1 == s2) );
        System.out.println("s1.equals(s2): " + String.valueOf(s1.equals(s2)));
        System.out.println("s1 == s3: " + String.valueOf(s1 == s3));
        System.out.println("s1.equals(s3): " + String.valueOf(s1.equals(s3)));
        System.out.println("s2 == s4: " + String.valueOf(s2 == s4));
        System.out.println("s2.equals(s4): " + String.valueOf(s2.equals(s4)));
        System.out.println("s5 == s6: " + String.valueOf(s5 == s6));
        System.out.println("s1 == s8: " + String.valueOf(s1 == s8));
    }
}
/* output
s1 == s2: false
s1.equals(s2): true
s1 == s3: true
s1.equals(s3): true
s2 == s4: false
s2.equls(s4): true
s5 == s6: true
s1 == s8: false
 */

從這段代碼的輸出可以看到,equals 比較的結(jié)果都是 true,這是因為 String 的 equals 比較的值( Object 對象的默認 equals 實現(xiàn)是比較引用,
String 對此方法進行了重寫)。== 比較的是兩個對象的引用,如果引用相同則返回 true,否則返回 false。s1==s2: falses2==s4: false
說明了 new 一個對象一定會生成一個新的引用返回。s1==s3: true 則證明了使用字面量創(chuàng)建對象同樣的字面量會得到同樣的引用。

s5 == s6 實際上和 s1 == s3 在 JVM 眼里是一樣的情況,因為早在編譯階段,這種常量的簡單運算就已經(jīng)完成了。我們可以使用 javap 反編譯一下 class 文件去查看
編譯后的情況。

? ~ javap -c StringConstPool.class
Compiled from "StringConstPool.java"
public class io.github.jshanet.thinkinginjava.constpool.StringConstPool {
  public io.github.jshanet.thinkinginjava.constpool.StringConstPool();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String hello world
       2: astore_1
       3: return
}

看不懂匯編也沒關(guān)系,因為注釋已經(jīng)很清楚了......

s1 == s8 的情況就略復(fù)雜,s8 是通過變量的運算而得,所以無法在編譯時直接算出其值。而 Java 又不能重載運算符,所以我們在 JDK 的源碼里也
找不到相關(guān)的線索。萬事不絕反編譯,我們再通過反編譯看看實際上編譯器對此是否有影響。

public class io.github.jshanet.thinkinginjava.constpool.StringConstPool {
  public io.github.jshanet.thinkinginjava.constpool.StringConstPool();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String hello
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String  world
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_2
      23: return
}

通過反編譯的結(jié)果可以發(fā)現(xiàn),String 的變量運算實際上在編譯后是由 StringBuilder 實現(xiàn)的,s8 = s7 + " world" 的代碼等價于
(new StringBuilder(s7)).append(" world").toString()Stringbuilder 是可變的類,通過 append 方法 和 toString 將兩個 String 對象聚合
成一個新的 String 對象,所以到這里就不難理解為什么 s1 == s8 : false 了。

之所以會有以上的效果,是因為有字符串常量池的存在。字符串對象的分配和其他對象一樣是要付出時間和空間代價,而字符串又是程序中最常用的對象,JVM
為了提高性能和減少內(nèi)存占用,引入了字符串的常量池,在使用字面量創(chuàng)建對象時, JVM 首先會去檢查常量池,如果池中有現(xiàn)成的對象就直接返回它的引用,如果
沒有就創(chuàng)建一個對象,并放到池里。因為字符串不可變的特性,所以 JVM 不用擔心多個變量引用同一個對象會改變對象的狀態(tài)。同時運行時實例創(chuàng)建的全局
字符串常量池中有一個表,總是為池中的每個字符串對象維護一個引用,所以這些對象不會被 GC 。

intern 方法的作用

上面說了很多都沒有涉及到主題 intern 方法,那么 intern 方法到作用到底是什么呢?首先查看一下源碼。

    /**
     * Returns a canonical representation for the string object.
     * 

* A pool of strings, initially empty, is maintained privately by the * class {@code String}. *

* When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. *

* It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. *

* All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * The Java™ Language Specification. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */ public native String intern();

Oracle JDK 中,intern 方法被 native 關(guān)鍵字修飾并且沒有實現(xiàn),這意味著這部分到實現(xiàn)是隱藏起來了。從注釋中看到,這個方法的作用是如果常量池
中存在當前字符串,就會直接返回當前字符串,如果常量池中沒有此字符串,會將此字符串放入常量池中后再返回。通過注釋的介紹已經(jīng)可以明白這個方法的作用了,
再用幾個例子證明一下。

public class StringConstPool {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = new String("hello");
        String s3 = s2.intern();
        System.out.println("s1 == s2: " + String.valueOf(s1 == s2));
        System.out.println("s1 == s3: " + String.valueOf(s1 == s3));
    }
}
/* output
s1 == s2: false
s1 == s3: true
*/

這里就很容易的了解 intern 實際上就是把普通的字符串對象也關(guān)聯(lián)到常量池中。

當然 intern 的實現(xiàn)原理和最佳實踐等也是需要理解學習的,美團技術(shù)團隊的這篇深入解析String#intern
很深入也很詳細,推薦閱讀。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/77126.html

相關(guān)文章

  • 一名3年工作經(jīng)驗的java程序員應(yīng)該具備的職業(yè)技能

    摘要:一名年工作經(jīng)驗的程序員應(yīng)該具備的技能,這可能是程序員們比較關(guān)心的內(nèi)容。數(shù)據(jù)結(jié)構(gòu)和算法分析數(shù)據(jù)結(jié)構(gòu)和算法分析,對于一名程序員來說,會比不會好而且在工作中能派上用場。 一名3年工作經(jīng)驗的Java程序員應(yīng)該具備的技能,這可能是Java程序員們比較關(guān)心的內(nèi)容。我這里要說明一下,以下列舉的內(nèi)容不是都要會的東西—-但是如果你掌握得越多,最終能得到的評價、拿到的薪水勢必也越高。 1、基本語法 這包括...

    renweihub 評論0 收藏0
  • 深入字節(jié)碼 -- 計算方法執(zhí)行時間

    摘要:什么是字節(jié)碼程序通過編譯之后生成文件就是字節(jié)碼集合正是有這樣一種中間碼字節(jié)碼,使得等函數(shù)語言只用實現(xiàn)一個編譯器即可運行在上。 什么是字節(jié)碼? java程序通過javac編譯之后生成文件.class就是字節(jié)碼集合,正是有這樣一種中間碼(字節(jié)碼),使得scala/groovy/clojure等函數(shù)語言只用實現(xiàn)一個編譯器即可運行在JVM上??纯匆欢魏唵未a。 public long ...

    娣辯孩 評論0 收藏0
  • Java中的DAO模式

    摘要:此的功能是從應(yīng)用程序中隱藏在底層存儲機制中執(zhí)行操作所涉及的所有復(fù)雜性。這正是模式試圖解決的問題。將模式與一起使用開發(fā)人員普遍認為的發(fā)布將模式的功能降級為零,因為該模式只是實體經(jīng)理提供的另一層抽象和復(fù)雜性。在這種情況下,模式有其自己的位置。 案例概述 數(shù)據(jù)訪問對象(DAO)模式是一種結(jié)構(gòu)模式,它允許我們使用抽象API將應(yīng)用程序/業(yè)務(wù)層與持久層(通常是關(guān)系數(shù)據(jù)庫,但它可以是任何其他持久性機...

    TIGERB 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<