String源碼閱讀

String源碼閱讀

wiki

學習目標

  • String在內(nèi)存中存在形式纺涤;
    • intern的用法宪肖,不同版本實現(xiàn)的差異
  • String的基本用法膨处;

內(nèi)存角度理解String

String str = new String("uranus"); // 堆中有String實例對象,常量池中有String實例

String str1 = "uranus"; //常量池中有string實例

String str2 = new String("uranus").intern(); //堆中有實例對象佩耳,常量池中有String實例對象

String str3 = "uranus" + "leon";//常量池中有uranusleon字符串的實例遂蛀,沒有uranus和leon的實例

String str1 = "uranus";
String str4 = str1 + "leon";//堆中有uranusleon的實例,常量池中沒有uranusleon的實例干厚,沒有l(wèi)eon的實例

String str5 = new String("uranus") + new String("leon"); // 堆中有uranus和leon的實例李滴,在常量池中是否有uranusleon這個字符串

new String("uranusleon")

package StringTest;

public class StringBasic {
    public static void main(String[] args)
    {
        String s1 = "uranusleon";
        String s2 = new String("uranusleon");
        String s3 = new String("uranusleon").intern();

        System.out.println(s1 == s2); // false
        System.out.println(s1 == s3); // true
    }
}

new String()生成了幾個變量

若常量池中已經(jīng)存在字面量,則直接引用蛮瞄,也就是此時只會創(chuàng)建一個對象所坯,如果常量池中不存在字面量,則先創(chuàng)建后引用挂捅,也就是有兩個

String字面量進入字符串常量池的時機

  • new String(“字面量”) 中 “字面量” 是何時進入字符串常量池的?

  • HotSpot VM的實現(xiàn)來說芹助,加載類的時候,那些字符串字面量會進入到當前類的運行時常量池闲先,不會進入全局的字符串常量池 ;

  • ldc指令觸發(fā)lazy resolution動作

    • 到當前類的運行時常量池(runtime constant pool状土,HotSpot VM里是ConstantPool + ConstantPoolCache)去查找該index對應(yīng)的項
    • 如果該項尚未resolve則resolve之,并返回resolve后的內(nèi)容伺糠。
    • 在遇到String類型常量時蒙谓,resolve的過程如果發(fā)現(xiàn)StringTable已經(jīng)有了內(nèi)容匹配的java.lang.String的引用,則直接返回這個引用;
    • 如果StringTable里尚未有內(nèi)容匹配的String實例的引用训桶,則會在Java堆里創(chuàng)建一個對應(yīng)內(nèi)容的String對象累驮,然后在StringTable記錄下這個引用酣倾,并返回這個引用出去。
  • 實驗代碼

    package StringTest;
    
    public class StringBasic {
        public static void main(String[] args) throws Exception {
            String string = new String("uranusleon");
        }
    }
    
  • 字節(jié)碼編譯

    public static void main(java.lang.String[]) throws java.lang.Exception;
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=2, args_size=1
             0: ldc           #2                  // String uranusleon
             2: astore_1
             3: return
          LineNumberTable:
            line 5: 0
            line 6: 3
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       4     0  args   [Ljava/lang/String;
                3       1     1 string   Ljava/lang/String;
        Exceptions:
          throws java.lang.Exception
    
    • ldc指令將在堆中創(chuàng)建一個堆中對應(yīng)于“uranusleon”的實例谤专,在字符串常量池中記錄引用灶挟。

String.intern()的理解

intern()的作用

當一個String實例調(diào)用intern()方法時,Java查找常量池中是否有相同Unicode的字符串常量箱叁,如果有墅垮,則返回其的引用,如果沒有耕漱,則在常量池中增加一個Unicode等于str的字符串并返回它的引用算色;

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");// 堆中創(chuàng)建對象“11”,String常量池中沒有“11”
    s3.intern(); //JDK6中在String常量池中新創(chuàng)建一個對象螟够,JDK7中在String常量池中保存堆中對象的引用
    String s4 = "11"; //JDK6中S4是常量池中對象的引用灾梦,JDK7中s4是堆中對象的引用
    System.out.println(s3 == s4); //JDK6中s3!=s4,JDK7中s3 == s4
}

打印結(jié)果

  • jdk6 下false false
  • jdk7下false true
public static void main(String[] args) {
    String s = new String("1");
    String s2 = "1";
    s.intern();
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1"); // 堆中創(chuàng)建對象“11”妓笙,String常量池中沒有“11”
    String s4 = "11"; //jdk6和jdk7都會在常量池中創(chuàng)建“11”的對象
    s3.intern(); // 發(fā)現(xiàn)常量池中有“11”若河,不需要新建或者保存堆中對象的引用
    System.out.println(s3 == s4);
}

打印結(jié)果

  • jdk6 下false false
  • jdk7下false false

原因

  • jdk7版本中將String常量池從Perm區(qū)移動到了java heap區(qū)域;
  • String.intern()方法執(zhí)行時寞宫,如果堆中存在對象萧福,會直接在String常量池中保存對象的引用,不會重新創(chuàng)建新的對象辈赋;

intern()的使用時機

對于可能經(jīng)常使用的字符串鲫忍,并且這些字符串在編譯期無法確定,只能在運行期才可以確定钥屈,可以使用intern()將字符串加入字符串常量池悟民。

static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];

public static void main(String[] args) throws Exception {
    Integer[] DB_DATA = new Integer[10];
    Random random = new Random(10 * 10000);
    for (int i = 0; i < DB_DATA.length; i++) {
        DB_DATA[i] = random.nextInt();
    }
    long t = System.currentTimeMillis();
    for (int i = 0; i < MAX; i++) {
        //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
         arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
    }

    System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}

intern()理解的要點

  • String str1 = new String("uranus") + new String("leon")執(zhí)行后,String intern1之前篷就,字符串常量池中有沒有"uranusleon"字符串的引用射亏;

    類的常量池中沒有“uranusleon”字面量,所以字符串常量池中沒有"uranusleon"字符串的引用

  • String str1 = new String("uranus") + new String("leon")String str1 = new String("uranusleon");的區(qū)別腻脏,單獨執(zhí)行后字符串常量池內(nèi)有哪些字符串鸦泳;

    • String str1 = new String("uranus") + new String("leon")執(zhí)行后字符串常量池有'uranus'和'leon'的引用,沒有'uranusleon'的引用
    • String str1 = new String("uranusleon");執(zhí)行后字符串常量池有'uranusleon'的引用
  • intern()方法執(zhí)行時字符串常量池內(nèi)是否有對應(yīng)的字符串永品;

由上述講解可以看出做鹰,在判斷字符串的引用在字符串常量池中是否存在主要看class文件的常量池中是否存在字符串的字面量。

String + 的實現(xiàn)

  • String + 是通過StringBuilder實現(xiàn)的

  • String str3 = str1 + str2

    對于字符串拼接鼎姐,如果有一個參數(shù)是變量钾麸,拼接是使用Stringbuilder.append更振,編譯期無法知道具體的字面量值,無法在字符串常量池中生成饭尝。

  • String str5 = "uranus" + "leon"

    對應(yīng)字符串拼接肯腕,如果兩個參數(shù)都是字面量,則直接編譯為拼接后的字符串钥平,字符串常量池中會生產(chǎn)uranusleon实撒。

String源碼

構(gòu)造方法

  • String是用字符數(shù)組char[]表示的

    /** The value is used for character storage. */
        private final char value[];
    
  • 使用字符數(shù)組構(gòu)造String

    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
    
    public String(char value[], int offset, int count) {
            if (offset < 0) {
                throw new StringIndexOutOfBoundsException(offset);
            }
            if (count <= 0) {
                if (count < 0) {
                    throw new StringIndexOutOfBoundsException(count);
                }
                if (offset <= value.length) {
                    this.value = "".value;
                    return;
                }
            }
            // Note: offset or count might be near -1>>>1.
            if (offset > value.length - count) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
            this.value = Arrays.copyOfRange(value, offset, offset+count);
        }
    
    • 使用Arrays.copyOfArrays.copyOfRange方法將字符數(shù)組的內(nèi)容復制到value[]
    • 可以只使用字符數(shù)組的一部分初始化String
    String(char[] value, boolean share) {
            // assert share : "unshared not supported";
            this.value = value;
        }
    
    • 此構(gòu)造方法參數(shù)share的作用是為了區(qū)分String(char[] value)方法;
    • 此方法構(gòu)造出來的String和參數(shù)傳過來的char[]共享一個數(shù)組涉瘾;
    • 優(yōu)點
      • 性能好:直接將數(shù)組引用賦值知态,不需要復制數(shù)組內(nèi)容,速度快立叛;
      • 共享數(shù)組節(jié)約內(nèi)存负敏;
      • 由于方法是protected的,對于調(diào)用他的方法來說秘蛇,由于無論是原字符串還是新字符串其做,其value數(shù)組本身都是String對象的私有屬性惫周,從外部是無法訪問的屉栓,因此對兩個字符串來說都很安全。
  • 使用字符串構(gòu)造String

    public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }
    
    • 直接將value和hash賦值給新String
  • 使用字節(jié)數(shù)組構(gòu)造String

    • byte是網(wǎng)絡(luò)傳輸或存儲的序列化形式客税。所以在很多傳輸和存儲的過程中需要將byte[]數(shù)組和String進行相互轉(zhuǎn)化艘策。
    String(byte bytes[]);
    String(byte bytes[], Charset charset);
    String(byte bytes[], int offset, int length);
    String(byte bytes[], int offset, int length, Charset charset);
    String(byte bytes[], int offset, int length, String charsetName);
    String(byte bytes[], String charsetName);
    
    • 調(diào)用構(gòu)造方法時需要指定編碼格式浮庐,如果不指定,默認為ISO-8859-1
  • 使用StringBuilder和StringBuffer構(gòu)造字符串

    public String(StringBuffer buffer) {
            synchronized(buffer) {
                this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
            }
        }
    
    public String(StringBuilder builder) {
            this.value = Arrays.copyOf(builder.getValue(), builder.length());
        }
    
    • 基本不會使用到柬焕,一般使用StringBuilder和StringBuffer的toString()方法审残。

比較方法

public boolean equals(Object anObject);
public boolean contentEquals(CharSequence cs);
public boolean contentEquals(StringBuffer sb);
public boolean equalsIgnoreCase(String anotherString);
public int compareTo(String anotherString);
public int compareToIgnoreCase(String str);
public boolean regionMatches(boolean ignoreCase, int toffset,
            String other, int ooffset, int len); //Tests if two string regions are equal.
public boolean regionMatches(int toffset, String other, int ooffset,int len);

equal()方法

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) { // value是私有的,怎么可以直接訪問斑举?
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  • private是針對類來說的搅轿,同一個類內(nèi)可以訪問相同類其他實例的私有變量;

  • 代碼提高效率的方法

    字符串相同:地址相同富玷;地址不同璧坟,但是內(nèi)容相同

    策略:將比較快速的部分(地址,比較對象類型)放在前面比較赎懦,速度慢的部分(比較字符數(shù)組)放在后面執(zhí)行雀鹃。

contentEquals()方法

public boolean contentEquals(CharSequence cs) {
        // Argument is a StringBuffer, StringBuilder
        if (cs instanceof AbstractStringBuilder) {
            if (cs instanceof StringBuffer) {
                synchronized(cs) {
                   return nonSyncContentEquals((AbstractStringBuilder)cs);
                }
            } else {
                return nonSyncContentEquals((AbstractStringBuilder)cs);
            }
        }
        // Argument is a String
        if (cs instanceof String) {
            return equals(cs);
        }
        // Argument is a generic CharSequence
        char v1[] = value;
        int n = v1.length;
        if (n != cs.length()) {
            return false;
        }
        for (int i = 0; i < n; i++) {
            if (v1[i] != cs.charAt(i)) {
                return false;
            }
        }
        return true;
    }
  • public boolean contentEquals(StringBuffer sb);實際調(diào)用了contentEquals(CharSequence cs)方法;

  • AbstractStringBuilderString都是接口CharSequence的實現(xiàn)励两,通過判斷輸入是AbstractStringBuilder還是String的實例黎茎,執(zhí)行不同的方法;

  • 比較的核心代碼

    for (int i = 0; i < n; i++) {
                if (v1[i] != v2[i]) {
                    return false;
                }
            }
    
    • 對字符串的字符數(shù)組中字符依次進行比較

equalsIgnoreCase(String anotherString)

public boolean equalsIgnoreCase(String anotherString) {
        return (this == anotherString) ? true
                : (anotherString != null)
                && (anotherString.value.length == value.length)
                && regionMatches(true, 0, anotherString, 0, value.length);
    }

compareTo()和compareToIgnoreCase()方法

  • 核心代碼是比較字符數(shù)組的每一個字符当悔;
  • compareToIgnoreCase()方法使用String的內(nèi)部類CaseInsensitiveComparator;

Hashcode()方法

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

subString()方法

  • public String substring(int beginIndex, int endIndex);
  • public String substring(int beginIndex);

調(diào)用public String(char value[], int offset, int count);方法生成一個新的String實例

replace方法

  • public String replace(char oldChar, char newChar);
  • public String replace(CharSequence target, CharSequence replacement);
  • public String replaceAll(String regex, String replacement);
  • public String replaceFirst(String regex, String replacement);

1)replace的參數(shù)是char和CharSequence,即可以支持字符的替換,也支持字符串的替換 2)replaceAll和replaceFirst的參數(shù)是regex,即基于規(guī)則表達式的替換,比如,可以通過replaceAll(“\d”, “*”)把一個字符串所有的數(shù)字字符都換成星號; 相同點是都是全部替換,即把源字符串中的某一字符或字符串全部換成指定的字符或字符串, 如果只想替換第一次出現(xiàn)的,可以使用 replaceFirst(),這個方法也是基于規(guī)則表達式的替換,但與replaceAll()不同的是,只替換第一次出現(xiàn)的字符串; 另外,如果replaceAll()和replaceFirst()所用的參數(shù)據(jù)不是基于規(guī)則表達式的,則與replace()替換字符串的效果是一樣的,即這兩者也支持字符串的操作;

codePointAt方法

public int codePointAt(int index);
public int codePointBefore(int index);
public int codePointCount(int beginIndex, int endIndex);
public int offsetByCodePoints(int index, int codePointOffset)傅瞻;
  • wiki

  • Code Point:A code point or code position is any of the numerical values that make up the code space.

  • String對象使用UTF-16表示Unicode字符,一般的符號只需要一個字符(兩個字節(jié))表示嗅骄,但是一些符號需要兩個字符(四個字節(jié))表示胳挎,這種表示方法稱為Surrogate,第一個字符叫Surrogate High溺森,第二個字符叫Surrogate Low慕爬。

  • Surrogate High的范圍是\uD800-\uDBFF,Surrogate Low的范圍是\uDC00-\uDFFF,在Unicode碼表中屏积,\uD800-\uDFFF只用來表示Surrogate Pair澡罚,不代表實際符號。

    Unicode range D800–DFFF is used for surrogate pairs in UTF-16 (used by Windows) and CESU-8 transformation formats, allowing these encodings to represent the supplementary plane code points, whose values are too large to fit in 16 bits. A pair of 16-bit code points — the first from the high surrogate area (D800–DBFF), and the second from the low surrogate area (DC00–DFFF) — are combined to form a 32-bit code point from the supplementary planes. Unicode and ISO/IEC 10646 do not assign actual characters to any of the code points in the D800–DFFF range — these code points only have meaning when used in surrogate pairs. Hence an individual code point from a surrogate pair does not represent a character, is invalid unless used in a surrogate pair, and is
    unconditionally invalid in UTF-32 and UTF-8 (if strict conformance to the standard is applied).


public int codePointAt(int index);的作用是返回索引出字符的Code Point肾请,如果此索引出的字符是Surrogate High,下一個索引的字符是Surrogate Low更胖,則返回Surrogate Pair對應(yīng)的Code Point铛铁。

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = new String(Character.toChars(uni));
    System.out.println(str.codePointAt(0)); //輸出128657
}
  • 符號?? 的Unicode碼為U+1F691,對應(yīng)的十進制數(shù)為128657,在String中需要兩個字符(Surrogate High和Surrogate Low)表示却妨;
  • 使用codePointAt()可以讀取兩個字符組成的Surrogate Pair對應(yīng)的Code Point饵逐。

public int codePointCount(int beginIndex, int endIndex);計算字符串的Char[]從beginIndexendIndex-1之間Code Point的數(shù)目(Surrogate Pair算為一個),Unpaired surrogates算為一個;

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = new String(Character.toChars(uni));
    System.out.println(str.codePointCount(0,1)); //輸出1
    System.out.println(str.length()); //輸出2
}
  • String.length()查詢的是字符數(shù)組的長度彪标,由于U+1F691需要兩個字符表示倍权,所有str.length() = 2;
  • codePointCount返回的是Code Point的個數(shù),由于字符串只有一個符號?? 捞烟,所有str.codePointCount(0,2)=1薄声;

public int codePointBefore(int index),如果字符數(shù)組中index-2的值是Surrogate High题画,index-1的值是Surrogate Low默辨,則返回index-2和index-1組成的Surrogate Pair的Code Point,否則只返回index-1對應(yīng)的code point苍息。

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = new String(Character.toChars(uni)) + "unicode";
    System.out.println(str.codePointBefore(2)); //輸出 128657
}
  • value[0]和value[1]可以組成Surrogate Pair缩幸,code point為128657

public int offsetByCodePoints(int index, int codePointOffset)方法返回String中從給定的index偏移codePointOfferSet個code points的索引

public static void main(String[] args)
{
    int uni = 0x1F691;
    String str = "uni" + new String(Character.toChars(uni)) + "code";
    System.out.println(str.offsetByCodePoints(0,4)); //輸出 5
}
  • 偏移4個code points后(uni占兩個字符,但是只有一個code point)竞思,所以偏移了5個字符表谊,輸出結(jié)果為5.

concat()方法

public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
  • 首先生成了一個字符數(shù)組buf[],將所有的字符放入新的字符數(shù)組盖喷;
  • 生產(chǎn)新的字符串爆办,其中的value[]直接指向buf[];

indexOf()

public int indexOf(int ch);
public int indexOf(int ch, int fromIndex);
private int indexOfSupplementary(int ch, int fromIndex);//indexOf(int ch, int fromIndex)調(diào)用
public int indexOf(String str);
public int indexOf(String str, int fromIndex);
static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex);
  • indxeOf()作用是找出字符在字符串中第一次出現(xiàn)的位置;
  • 假設(shè)indexOf(int ch)indexOf(int ch, int fromIndex)的結(jié)果為k课梳,如果ch > 0xFFFF押逼,則codePointAt(K) = ch步藕,否則charAt(K) = ch

matches()方法

String.matches()方法匹配整個字符串是否符合正則表達式挑格,不是匹配部分字符串

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末咙冗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漂彤,更是在濱河造成了極大的恐慌雾消,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挫望,死亡現(xiàn)場離奇詭異立润,居然都是意外死亡,警方通過查閱死者的電腦和手機媳板,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門桑腮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蛉幸,你說我怎么就攤上這事破讨。” “怎么了奕纫?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵提陶,是天一觀的道長。 經(jīng)常有香客問我匹层,道長隙笆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任升筏,我火速辦了婚禮撑柔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘您访。我一直安慰自己乏冀,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布洋只。 她就那樣靜靜地躺著辆沦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪识虚。 梳的紋絲不亂的頭發(fā)上肢扯,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音担锤,去河邊找鬼蔚晨。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的铭腕。 我是一名探鬼主播银择,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼累舷!你這毒婦竟也來了浩考?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤被盈,失蹤者是張志新(化名)和其女友劉穎析孽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體只怎,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡袜瞬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了身堡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邓尤。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贴谎,靈堂內(nèi)的尸體忽然破棺而出汞扎,到底是詐尸還是另有隱情,我是刑警寧澤赴精,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站绞幌,受9級特大地震影響蕾哟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莲蜘,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一谭确、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧票渠,春花似錦逐哈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至杜窄,卻和暖如春肠骆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背塞耕。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工蚀腿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扫外。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓莉钙,卻偏偏與公主長得像廓脆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子磁玉,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前言 最先接觸編程的知識是在大學里面蜀涨,大學里面學了一些基礎(chǔ)的知識瞎嬉,c語言,java語言厚柳,單片機的匯編語言等氧枣;大學畢...
    oceanfive閱讀 3,044評論 0 7
  • 從網(wǎng)上復制的,看別人的比較全面别垮,自己搬過來便监,方便以后查找。原鏈接:https://www.cnblogs.com/...
    lxtyp閱讀 1,344評論 0 9
  • 一碳想、Java 簡介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O(shè)計...
    子非魚_t_閱讀 4,154評論 1 44
  • 成為伸手黨的一個非常根本的原因是烧董,其實他還沒有養(yǎng)成獨立思考的習慣,心理依賴性極強胧奔。 比如群聊里面逊移,總是有人會去問在...
    我是林路閱讀 227評論 0 1
  • 為了理解React的工作過程,我們就必須要了解React組件的生命周期龙填,如同人有生老病死胳泉,每個組件在網(wǎng)頁中也會被創(chuàng)...
    蚊小文閱讀 256評論 0 0