標籤:
在此之前,我們已經討論過了成員內部類可以無條件訪問外部類的成員,那具體究竟是如何?的呢?下面通過反編譯位元組碼檔案看看究竟。事實上,編譯器在進行編譯的時候,會將成員內部類單獨編譯成一個位元組碼檔案,下面是Outter.java的代碼:
public class Outter { private Inner inner = null; public Outter() { } public Inner getInnerInstance() { if(inner == null) inner = new Inner(); return inner; } protected class Inner { public Inner() { } }}
編譯之後,出現了兩個位元組碼檔案:
反編譯Outter$Inner.class檔案得到下面資訊:
E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$InnerCompiled from "Outter.java"public class com.cxh.test2.Outter$Inner extends java.lang.Object SourceFile: "Outter.java" InnerClass: #24= #1 of #22; //Inner=class com/cxh/test2/Outter$Inner of class com/cxh/test2/Outter minor version: 0 major version: 50 Constant pool:const #1 = class #2; // com/cxh/test2/Outter$Innerconst #2 = Asciz com/cxh/test2/Outter$Inner;const #3 = class #4; // java/lang/Objectconst #4 = Asciz java/lang/Object;const #5 = Asciz this$0;const #6 = Asciz Lcom/cxh/test2/Outter;;const #7 = Asciz <init>;const #8 = Asciz (Lcom/cxh/test2/Outter;)V;const #9 = Asciz Code;const #10 = Field #1.#11; // com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/test2/Outter;const #11 = NameAndType #5:#6;// this$0:Lcom/cxh/test2/Outter;const #12 = Method #3.#13; // java/lang/Object."<init>":()Vconst #13 = NameAndType #7:#14;// "<init>":()Vconst #14 = Asciz ()V;const #15 = Asciz LineNumberTable;const #16 = Asciz LocalVariableTable;const #17 = Asciz this;const #18 = Asciz Lcom/cxh/test2/Outter$Inner;;const #19 = Asciz SourceFile;const #20 = Asciz Outter.java;const #21 = Asciz InnerClasses;const #22 = class #23; // com/cxh/test2/Outterconst #23 = Asciz com/cxh/test2/Outter;const #24 = Asciz Inner; {final com.cxh.test2.Outter this$0; public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: putfield #10; //Field this$0:Lcom/cxh/test2/Outter; 5: aload_0 6: invokespecial #12; //Method java/lang/Object."<init>":()V 9: return LineNumberTable: line 16: 0 line 18: 9 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/cxh/test2/Outter$Inner; }
第11行到35行是常量池的內容,下面先看看第38行的內容:
final com.cxh.test2.Outter this$0;
這行是一個指向外部類對象的指標,看到這裡想必大家豁然開朗了。也就是說編譯器會預設為成員內部類添加了一個指向外部類對象的引用,那麼這個引用是如何賦初值的呢?下面接著看內部類的構造器:
public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);
從這裡可以看出,雖然我們在定義的內部類的構造器是無參構造器,編譯器還是會預設添加一個參數,該參數的類型為指向外部類對象的一個引用,所以成員內部類中的Outter this&0 指標便指向了外部類對象,因此可以在成員內部類中隨意訪問外部類的成員。從這裡也間接說明了成員內部類是依賴於外部類的,如果沒有建立外部類的對象,則無法對Outter this&0引用進行初始化賦值,也就無法建立成員內部類的對象了。
Java基礎-內部類-為什麼成員內部類可以無條件訪問外部類