Java漫談-String下

本文首發(fā):windCoder.com

上篇介紹了一些String的基礎(chǔ)與簡(jiǎn)單的創(chuàng)建方式褒链,本篇引入它的intern()方法锌妻。

關(guān)于intern()方法

當(dāng)一個(gè)String實(shí)例str調(diào)用intern()方法時(shí)港华,如果常量池中已經(jīng)有了這個(gè)字符串,那么直接返回常量池中它的引用,如果沒有糕非,那就將它的引用保存一份到字符串常量池,然后直接返回這個(gè)引用球榆⌒喾剩可參考JDK中的解釋或The Java Virtual Machine Specification, Java SE 8 Edition (§5.1),簡(jiǎn)單來說就是一個(gè)可以手動(dòng)將未存在常量池的字符串存入常量池并返回其引用的方法持钉。

示例3

現(xiàn)在再來看另一種方式創(chuàng)建String的例子:

public class StringDemo3 {
    public static void main(String[] args) {
        String s1 = new String("1") + new String("a"); // 1
        s1.intern();          // 2
        String s2 = "1a";    // 3
        System.out.println(s1 == s2);     // 4
        System.out.println(s1.intern() == s2); // 5
        String s3 = "1"+"a";
        System.out.println(s3 == s2); // 6
    }
}

運(yùn)行結(jié)果

true
true
true

解析

語句6肯定是true衡招,因?yàn)榫幾g器會(huì)對(duì)"1"+"a"進(jìn)行優(yōu)化,使其在編譯完成后成為"1a",即String s3 = "1a";每强,從而導(dǎo)致s3和s2均為字符串常量池中的字符串的引用始腾,通過字節(jié)碼也能看到類似情形:

// ... 省略

// s2
40: ldc           #11                 // String 1a
42: astore_2

// ...省略

// s3
78: ldc           #11                 // String 1a
80: astore_3

// ...省略

現(xiàn)在我們看下String s1 = new String("1") + new String("a");的字節(jié)碼:

0: new           #2                  // class java/lang/StringBuilder
3: dup
4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
7: new           #4                  // class java/lang/String
10: dup
11: ldc           #5                  // String 1
13: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: new           #4                  // class java/lang/String
22: dup
23: ldc           #8                  // String a
25: invokespecial #6                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_1

1.通過字節(jié)碼可知,字符串常量池中存在"1","a"兩個(gè)常量空执,字符串s1的內(nèi)部實(shí)現(xiàn)是通過StringBuilder執(zhí)行append方法拼接后執(zhí)行toString()獲得的浪箭。這一段代碼不算常量池中的一共創(chuàng)建了3個(gè)對(duì)象,一個(gè)StringBuilder,一個(gè)String 1,一個(gè)String a,最后在僅堆中生成引用s1所指向的字符串“1a”辨绊,此時(shí)常量池中并無“1a”奶栖。

2.當(dāng)執(zhí)行s1.intern();時(shí),發(fā)現(xiàn)常量池中不存在“1a”门坷,故在常量池中復(fù)制一份與s1相同的引用宣鄙,即直接將s1鎖指向的字符串“1a”的地址復(fù)制一份到常量池中。

3.此時(shí)再執(zhí)行String s2 = "1a";默蚌,拿到的就和s1相同了冻晤,從而有了語句3和4的true。

如果將語句1和2對(duì)調(diào)敏簿,則會(huì)出現(xiàn)結(jié)果:

false
true
true

問題

按照上文所言明也,在類加載階段“1a”應(yīng)該已經(jīng)與"1","a"兩個(gè)常量一樣宣虾,被加載了,為何上面的解說1中最后說此時(shí)常量池中并無“1a”呢温数?

解惑

其實(shí)這涉及到類加載階段中的resolve階段绣硝,這個(gè)階段會(huì)解析Class文件中常量并在字符串常量池中駐留其引用。但是撑刺,該過程是lazy resolve的鹉胖,而觸發(fā)執(zhí)行加載的命令就是ldc

ldc指令是否需要?jiǎng)?chuàng)建新的String實(shí)例够傍,全看在第一次執(zhí)行該指令時(shí)甫菠,字符串常量池中是否已經(jīng)記錄了一個(gè)對(duì)應(yīng)內(nèi)容的String的引用。

在StringDemo3中冕屯,執(zhí)行s1.intern();時(shí)寂诱,第一次執(zhí)行了ldc,此時(shí)查找字符串常量池安聘,發(fā)現(xiàn)沒有對(duì)應(yīng)內(nèi)容的String的引用痰洒,故直接使用了s1的引用。

若是將語句2和3互換浴韭,此時(shí)屬于第一次執(zhí)行針對(duì)1aldc指令丘喻,此時(shí)查找字符串常量池,發(fā)現(xiàn)沒有對(duì)應(yīng)內(nèi)容的String的引用念颈,故創(chuàng)建新的String實(shí)例泉粉,將引用存入字符串常量池中一份并返回給s2,如此s1 == s2的結(jié)果將為false榴芳。

實(shí)例4

現(xiàn)在我們看下《深入理解Java虛擬機(jī)》中的一個(gè)例子:

public class StringDemo4 {
    public static void main(String[] args) {
        String str1 = new StringBuilder("計(jì)算機(jī)").append("軟件").toString();
        System.out.println(str1.intern() == str1);

        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern() == str2);
    }
}

結(jié)果

true
false

原文解析
原文中如是說:

這段代碼在JDK1.6中運(yùn)行嗡靡,會(huì)得到兩個(gè)false,而在JDK1.7中運(yùn)行翠语,會(huì)得到一個(gè)true和一個(gè)false叽躯。產(chǎn)生差異的原因是:

在JDK1.6中,intern()方法會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代中肌括,返回的也是永久代中這個(gè)字符串實(shí)例的引用点骑,而由StringBuilder創(chuàng)建的字符串實(shí)例在Java堆上,所以必然是兩個(gè)不同的引用谍夭,將返回false黑滴。

而JDK1.7(以及其他部分虛擬機(jī),例如JRockit)的intern()實(shí)現(xiàn)不會(huì)再復(fù)制實(shí)例紧索,只是在常量池中記錄首次出現(xiàn)的實(shí)例引用袁辈。因此intern()返回的引用和由StringBuilder創(chuàng)建的那個(gè)字符串實(shí)例是同一個(gè)。

對(duì)于str2比較返回false是因?yàn)椤癹ava”字符串在執(zhí)行StringBuilder.toString()之前已經(jīng)出現(xiàn)過珠漂,字符串常量池中已經(jīng)有它的引用了晚缩,不符合“首次出現(xiàn)”的原則尾膊,而“計(jì)算機(jī)軟件”這個(gè)字符串則是首次出現(xiàn)的,因此返回true.

解析
原文說“java”這個(gè)字符串之前已經(jīng)出現(xiàn)過荞彼,卻沒說在哪出現(xiàn)過冈敛。

RednaxelaFX在基于OpenJDK 7u45的實(shí)驗(yàn)中發(fā)現(xiàn)其來自sun.misc.Version類,在Oracle JDK7u / OpenJDK7u里的HotSpot VM會(huì)通過該類獲取JDk的名稱和具體版本信息鸣皂。

擴(kuò)展

1. 常量池

嚴(yán)格來說抓谴,Java中存在著3中常量池:

  • Class常量池(Class文件中的The Constant Pool)
  • 運(yùn)行時(shí)常量池(Run-Time Constant Pool)
  • 字符串常量池(String Pool/string literal pool,有時(shí)也稱String池/全局字符串池)

1.1 Class常量池

Class文件中的除了有類的版本、字段寞缝、方法癌压、接口等描述信息,還有一項(xiàng)時(shí)常量池(Constant Pool Table)荆陆,這里面主要存放兩大類常量:字面量(Literal)符號(hào)引用(Symbolic References)滩届。

字面量比較接近于Java語言層面的常量概念,如:

  • 文本字符串
  • 聲明為final的常量值等慎宾。

符號(hào)引用屬于編譯原理方面的概念丐吓,包含下面三類常量:

  • 類和接口的全限定名(Fully Qualified Name)
  • 字段的名稱和描述(Descriptor)
  • 方法的名稱和描述

可以通過上一節(jié)中的javap命令查看浅悉,以將上面的StringDemo3反編譯為例趟据,格式類似如下:

Constant pool:
   #1 = Methodref          #15.#37        // java/lang/Object."<init>":()V
   #2 = Class              #38            // java/lang/StringBuilder
   #3 = Methodref          #2.#37         // java/lang/StringBuilder."<init>":()V
   #4 = Class              #39            // java/lang/String
   #5 = String             #40            // 1
   #6 = Methodref          #4.#41         // java/lang/String."<init>":(Ljava/lang/String;)V
   #7 = Methodref          #2.#42         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = String             #43            // a
   #9 = Methodref          #2.#44         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #4.#45         // java/lang/String.intern:()Ljava/lang/String;
  #11 = String             #46            // 1a
  #12 = Fieldref           #47.#48        // java/lang/System.out:Ljava/io/PrintStream;
  #13 = Methodref          #49.#50        // java/io/PrintStream.println:(Z)V
  #14 = Class              #51            // Others/base/StringDemo4
  #15 = Class              #52            // java/lang/Object
  #16 = Utf8               <init>
  #17 = Utf8               ()V
  #18 = Utf8               Code
  #19 = Utf8               LineNumberTable
  #20 = Utf8               LocalVariableTable
  #21 = Utf8               this
  #22 = Utf8               LOthers/base/StringDemo4;
  #23 = Utf8               main
  #24 = Utf8               ([Ljava/lang/String;)V
  #25 = Utf8               args
  #26 = Utf8               [Ljava/lang/String;
  #27 = Utf8               s1
  #28 = Utf8               Ljava/lang/String;
  #29 = Utf8               s2
  #30 = Utf8               s3
  #31 = Utf8               StackMapTable
  #32 = Class              #26            // "[Ljava/lang/String;"
  #33 = Class              #39            // java/lang/String
  #34 = Class              #53            // java/io/PrintStream
  #35 = Utf8               SourceFile
  #36 = Utf8               StringDemo4.java
  #37 = NameAndType        #16:#17        // "<init>":()V
  #38 = Utf8               java/lang/StringBuilder
  #39 = Utf8               java/lang/String
  #40 = Utf8               1
  #41 = NameAndType        #16:#54        // "<init>":(Ljava/lang/String;)V
  #42 = NameAndType        #55:#56        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #43 = Utf8               a
  #44 = NameAndType        #57:#58        // toString:()Ljava/lang/String;
  #45 = NameAndType        #59:#58        // intern:()Ljava/lang/String;
  #46 = Utf8               1a
  #47 = Class              #60            // java/lang/System
  #48 = NameAndType        #61:#62        // out:Ljava/io/PrintStream;
  #49 = Class              #53            // java/io/PrintStream
  #50 = NameAndType        #63:#64        // println:(Z)V
  #51 = Utf8               Others/base/StringDemo4
  #52 = Utf8               java/lang/Object
  #53 = Utf8               java/io/PrintStream
  #54 = Utf8               (Ljava/lang/String;)V
  #55 = Utf8               append
  #56 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #57 = Utf8               toString
  #58 = Utf8               ()Ljava/lang/String;
  #59 = Utf8               intern
  #60 = Utf8               java/lang/System
  #61 = Utf8               out
  #62 = Utf8               Ljava/io/PrintStream;
  #63 = Utf8               println
  #64 = Utf8               (Z)V

由上可以看到涉及到String的有兩個(gè)類型,CONSTANT_Utf8CONSTANT_String术健。

CONSTANT_Utf8汹碱,即 CONSTANT_Utf8_info Structure,是一個(gè)用于表示常量字符串值的荞估,這里真正持有字符串內(nèi)容咳促。(§4.4.7)

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

CONSTANT_String, 即CONSTANT_String_info Structure勘伺,該類型用于表示該類型的常量對(duì)象String跪腹。其不直接持有字符串內(nèi)容,而是持有一個(gè)string_index飞醉,string_index該必須是constant_pool表中的有效索引冲茸,該constant_pool索引處的條目必須是一個(gè)CONSTANT_Utf8_info結(jié)構(gòu),即為一個(gè)CONSTANT_Utf8類型的常量缅帘,從而持有字符串內(nèi)容轴术。(§4.4.3)

CONSTANT_String_info { 
    u1 tag; 
    u2 string_index; 
}

1.2 運(yùn)行時(shí)常量池

方法區(qū)的一部分。Class文件的常量池(上面的1.1)中的內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放钦无。

每個(gè)運(yùn)行時(shí)常量池都是從Java虛擬機(jī)的方法區(qū)域(§2.5.4)中分配的逗栽。當(dāng)Java虛擬機(jī)創(chuàng)建類或接口(§5.3)時(shí),將構(gòu)造類或接口的運(yùn)行時(shí)常量池(§2.5.5)失暂。

A run-time constant pool is a per-class or per-interface run-time representation of the constant_pool table in a class file (§4.4). It contains several kinds of constants, ranging from numeric literals known at compile-time to method and field references that must be resolved at run-time. The run-time constant pool serves a function similar to that of a symbol table for a conventional programming language, although it contains a wider range of data than a typical symbol table.

Each run-time constant pool is allocated from the Java Virtual Machine's method area (§2.5.4). The run-time constant pool for a class or interface is constructed when the class or interface is created (§5.3) by the Java Virtual Machine.

這意味著每個(gè)類/接口都會(huì)有一個(gè)運(yùn)行時(shí)常量池彼宠。

1.3 字符串常量池

HotSpot VM里鳄虱,記錄interned string的一個(gè)全局表叫做StringTable,它本質(zhì)上就是個(gè)HashSet<String>凭峡。這是個(gè)純運(yùn)行時(shí)的結(jié)構(gòu)醇蝴,而且是惰性(lazy)維護(hù)的。注意它只存儲(chǔ)對(duì)java.lang.String實(shí)例的引用想罕,而不存儲(chǔ)String對(duì)象的內(nèi)容悠栓。

一般我們說一個(gè)字符串進(jìn)入了全局的字符串常量池其實(shí)是說在這個(gè)StringTable中保存了對(duì)它的引用,反之按价,如果說沒有在其中就是說StringTable中沒有對(duì)它的引用惭适。

字面量進(jìn)入字符串常量池的時(shí)機(jī)

通過上篇文章,我們可以得到如下兩個(gè)結(jié)論:

1.StringDemo3.class 的 class文件常量池 中 是含有 "1" ,"a","1a"的楼镐。

2.在類加載階段癞志, JVM會(huì)在堆中創(chuàng)建 對(duì)應(yīng)這些 class文件常量池中的 字符串對(duì)象實(shí)例 并在字符串常量池中駐留其引用。具體在resolve階段執(zhí)行框产。這些常量全局共享凄杯。

但resolve階段實(shí)際上并不是立即就創(chuàng)建對(duì)象并且在字符串常量池中駐留了引用。 JVM規(guī)范里明確指定resolve階段可以是lazy的[(§5.4)]:(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4)

...

Resolution of symbolic references in the class or interface is an optional part of linking.
...

a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution). This means that the resolution process may continue, in some implementations, after a class or interface has been initialized. Whichever strategy is followed, any error detected during resolution must be thrown at a point in the program that (directly or indirectly) uses a symbolic reference to the class or interface.

在HotSpot VM中秉宿,運(yùn)行時(shí)常量池里:

  • CONSTANT_Utf8 -> Symbol*(一個(gè)指針戒突,指向一個(gè)Symbol類型的C++對(duì)象,內(nèi)容是跟Class文件同樣格式的UTF-8編碼的字符串)
  • CONSTANT_String -> java.lang.String(一個(gè)實(shí)際的Java對(duì)象的引用描睦,C++類型是oop)

CONSTANT_Utf8會(huì)在類加載的過程中就全部創(chuàng)建出來膊存,而CONSTANT_String則是lazy resolve的。例如在第一次引用該項(xiàng)的ldc指令被第一次執(zhí)行到的時(shí)候才會(huì)resolve忱叭。

在尚未resolve的時(shí)候隔崎,HotSpot VM把它的類型叫做JVM_CONSTANT_UnresolvedString,內(nèi)容跟Class文件里一樣只是一個(gè)index韵丑;等到resolve過后這個(gè)項(xiàng)的常量類型就會(huì)變成最終的JVM_CONSTANT_String爵卒,而內(nèi)容則變成實(shí)際的那個(gè)oop。

即撵彻,就HotSpot VM的實(shí)現(xiàn)來說钓株,加載類的時(shí)候,那些字符串字面量會(huì)進(jìn)入到當(dāng)前類的運(yùn)行時(shí)常量池千康,不會(huì)進(jìn)入全局的字符串常量池(即在StringTable中并沒有相應(yīng)的引用享幽,在堆中也沒有對(duì)應(yīng)的對(duì)象產(chǎn)生)。

再談ldc指令

根據(jù)上篇文章我們已知ldc 將int, float或String型常量值從常量池中推送至棧頂拾弃。但值桩,根據(jù)本文上面說的,在類加載階段豪椿,這個(gè) resolve 階段( constant pool resolution )是lazy的奔坟。即在resolve階段之前并沒有真正的對(duì)象携栋,字符串常量池里自然也沒有對(duì)應(yīng)的引用。那么ldc指令還怎么把人推送至棧頂咳秉?或者換一個(gè)角度想婉支,既然resolve 階段是lazy的,那總有一個(gè)時(shí)候它要真正的執(zhí)行吧澜建,是什么時(shí)候向挖?

執(zhí)行l(wèi)dc指令就是觸發(fā)這個(gè)lazy resolution動(dòng)作的條件

ldc字節(jié)碼在這里的執(zhí)行語義是:

  • 到當(dāng)前類的運(yùn)行時(shí)常量池(runtime constant pool炕舵,HotSpot VM里是ConstantPool + ConstantPoolCache)去查找該index對(duì)應(yīng)的項(xiàng)何之。
  • 如果該項(xiàng)尚未resolve,則resolve之咽筋,并返回resolve后的內(nèi)容溶推。
  • 在遇到String類型常量時(shí),resolve的過程如果發(fā)現(xiàn)StringTable已經(jīng)有了內(nèi)容匹配的java.lang.String的引用奸攻,則直接返回這個(gè)引用蒜危。
  • 反之,如果StringTable里尚未有內(nèi)容匹配的String實(shí)例的引用睹耐,則會(huì)在Java堆里創(chuàng)建一個(gè)對(duì)應(yīng)內(nèi)容的String對(duì)象辐赞,然后在StringTable記錄下這個(gè)引用,并返回這個(gè)引用出去疏橄。

可見占拍,ldc指令是否需要?jiǎng)?chuàng)建新的String實(shí)例,全看在第一次執(zhí)行這一條ldc指令時(shí)捎迫,StringTable是否已經(jīng)記錄了一個(gè)對(duì)應(yīng)內(nèi)容的String的引用。

參考資料

  1. Java 中new String("字面量") 中 "字面量" 是何時(shí)進(jìn)入字符串常量池的?
  2. The Java? Virtual Machine Specification Java SE 8 Edition
  3. 《深入理解 Java 虛擬機(jī)》第二版
  4. 如何理解《深入理解java虛擬機(jī)》第二版中對(duì)String.intern()方法的講解中所舉的例子表牢?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窄绒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子崔兴,更是在濱河造成了極大的恐慌彰导,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敲茄,死亡現(xiàn)場(chǎng)離奇詭異位谋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)堰燎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門掏父,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秆剪,你說我怎么就攤上這事赊淑【粽” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵陶缺,是天一觀的道長(zhǎng)钾挟。 經(jīng)常有香客問我,道長(zhǎng)饱岸,這世上最難降的妖魔是什么掺出? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮苫费,結(jié)果婚禮上蛛砰,老公的妹妹穿的比我還像新娘。我一直安慰自己黍衙,他們只是感情好泥畅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著琅翻,像睡著了一般位仁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上方椎,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天聂抢,我揣著相機(jī)與錄音,去河邊找鬼棠众。 笑死琳疏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闸拿。 我是一名探鬼主播空盼,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼新荤!你這毒婦竟也來了揽趾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤苛骨,失蹤者是張志新(化名)和其女友劉穎篱瞎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痒芝,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俐筋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了严衬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澄者。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闷哆,到底是詐尸還是另有隱情腰奋,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布抱怔,位于F島的核電站劣坊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏屈留。R本人自食惡果不足惜局冰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灌危。 院中可真熱鬧康二,春花似錦、人聲如沸勇蝙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽味混。三九已至产雹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翁锡,已是汗流浹背蔓挖。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留馆衔,地道東北人瘟判。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像角溃,于是被迫代替她去往敵國(guó)和親拷获。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • ??需要說明的一點(diǎn)是开镣,這篇文章是以《深入理解Java虛擬機(jī)》第二版這本書為基礎(chǔ)的刀诬,這里假設(shè)大家已經(jīng)了解了JVM的運(yùn)...
    Geeks_Liu閱讀 14,013評(píng)論 5 44
  • String源碼閱讀 wiki 通過反編譯深入理解Java String及intern 成神之路-基礎(chǔ)篇 Java...
    uranusleon閱讀 737評(píng)論 0 1
  • 字節(jié)碼查看工具:WinHex 前言 Java虛擬機(jī)實(shí)現(xiàn)語言無關(guān)性的基石就是Class文件Java虛擬機(jī)提供的語言無...
    zlcook閱讀 7,131評(píng)論 4 18
  • 其他更多java基礎(chǔ)文章:java基礎(chǔ)學(xué)習(xí)(目錄) 學(xué)習(xí)資料:String類API中文深入解析String#int...
    Hiwayz閱讀 982評(píng)論 0 1
  • ?每一個(gè)class文件都對(duì)應(yīng)著唯一一個(gè)類或者接口的定義信息,但是相對(duì)地邪财,類或者接口并不一定都必須定義在文件里(比如...
    SunnyMore閱讀 6,099評(píng)論 0 1