我們以單例靜態內部類模式來看:代碼如下
public class S4{
private S4(){}
private static class S5{
private static S4 s4 = new S4();
}
public static S4 getS4(){
return S5.s4;
}
}
如上圖所示:S4在內部引用了內部類S5的私有屬性。要理解這個問題需要從字節碼文件層面來分析:
我們將S4和S5進行反編譯如下圖:使用javap -c -v S4.class
Last modified 2020-4-7; size 385 bytes
MD5 checksum 07f86ad5660fd61e1047ed507eb63a4a
Compiled from "S4.java"
public class S4
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#19 // S4."<init>":()V
#2 = Methodref #5.#19 // java/lang/Object."<init>"
#3 = Methodref #8.#20 // S4$S5.access$100:()LS4;
#4 = Class #21 // S4
#5 = Class #22 // java/lang/Object
#6 = Class #23 // S4$1
#7 = Utf8 InnerClasses
#8 = Class #24 // S4$S5
#9 = Utf8 S5
#10 = Utf8 <init>
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 getS4
#15 = Utf8 ()LS4;
#16 = Utf8 (LS4$1;)V
#17 = Utf8 SourceFile
#18 = Utf8 S4.java
#19 = NameAndType #10:#11 // "<init>":()V
#20 = NameAndType #25:#15 // access$100:()LS4;
#21 = Utf8 S4
#22 = Utf8 java/lang/Object
#23 = Utf8 S4$1
#24 = Utf8 S4$S5
#25 = Utf8 access$100
{
public static S4 getS4();
descriptor: ()LS4;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: invokestatic #3 // Method S4$S5.access$1
3: areturn
LineNumberTable:
line 7: 0
S4(S4$1);
descriptor: (LS4$1;)V
flags: ACC_SYNTHETIC
Code:
stack=1, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method "<init>":()V
4: return
LineNumberTable:
line 1: 0
}
SourceFile: "S4.java"
InnerClasses:
static #6; //class S4$1
當調用S4的getS4()方法時其實是調用S5的.access$100方法,我們再來看看這個.access$100:是什麼。使用反彙編命令:
javap -c -v S4$S5.class
Last modified 2020-4-7; size 420 bytes
MD5 checksum 7511336207b6a42f87c79e3c5c05dbd2
Compiled from "S4.java"
class S4$S5
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Fieldref #5.#18 // S4$S5.s4:LS4;
#2 = Methodref #6.#19 // java/lang/Object."<init>":()V
#3 = Class #20 // S4
#4 = Methodref #3.#21 // S4."<init>":(LS4$1;)V
#5 = Class #22 // S4$S5
#6 = Class #25 // java/lang/Object
#7 = Utf8 s4
#8 = Utf8 LS4;
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 access$100
#14 = Utf8 ()LS4;
#15 = Utf8 <clinit>
#16 = Utf8 SourceFile
#17 = Utf8 S4.java
#18 = NameAndType #7:#8 // s4:LS4;
#19 = NameAndType #9:#10 // "<init>":()V
#20 = Utf8 S4
#21 = NameAndType #9:#27 // "<init>":(LS4$1;)V
#22 = Utf8 S4$S5
#23 = Utf8 S5
#24 = Utf8 InnerClasses
#25 = Utf8 java/lang/Object
#26 = Class #28 // S4$1
#27 = Utf8 (LS4$1;)V
#28 = Utf8 S4$1
{
static S4 access$100();
descriptor: ()LS4;
flags: ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field s4:LS4;
3: areturn
LineNumberTable:
line 3: 0
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=0, args_size=0
0: new #3 // class S4
3: dup
4: aconst_null
5: invokespecial #4 // Method S4."<init>":(LS4$1;)V
8: putstatic #1 // Field s4:LS4;
11: return
LineNumberTable:
line 4: 0
}
SourceFile: "S4.java"
InnerClasses:
static #26; //class S4$1
如上圖所示:S5首先持有一個S4對象,如變量池#1所示,然後再看access$100方法,這個方法會調用字節碼命令getstatic直接返回其持有的靜態對象S4。所以到這裏我們就明白了整個邏輯對象,getS4->access$100->S4 。