外部類如何調用內部類private

我們以單例靜態內部類模式來看:代碼如下

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 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章