使用工具:
Java 8
IDEA 2018
1. 內部類的設計原因
①內部類方法可以訪問外部類的屬性,包括私有屬性(將內部類定義成單獨的外部類,則需要提供訪問域的public方法)
②內部類可以對同一個包中的其他類隱藏起來(內部類可以是外部類私有的,而外部類的權限只可以是包、public)
③當想要定義一個回調函數且不想編寫大量代碼時,使用匿名(anonymous)內部類比較便捷。
注意:內部類可以訪問外部類的屬性,而外部類不能訪問內部類的屬性。
舉個例子:
public class OuterAndInnerClass { private int number; public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); inner.getOuterField(); } class InnerClass{ private String name; public void getOuterField(){ System.out.println("inner class: " + number); // inner class: 0 } } }
內部類調用外部類的private屬性,是可以的。這個內部類inner對象是屬於外部類outer對象的內部類對象。
再來看看外部類調用內部類的情況:

可以看到,無法調用到內部類的屬性,因為沒有內部類的對象,我們構建一個內部類參數傳入方法中,試試
public class OuterAndInnerClass { private int number; public void getInnerClassField(InnerClass inner){ System.out.println(inner.name); } class InnerClass{ private String name = "name"; public void getOuterField(){ System.out.println("inner class: " + number); // inner class: 0 } } public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); // inner.getOuterField(); outer.getInnerClassField(inner); // name } }
雖然可以調用到內部類的屬性,但是這個作為方法參數傳入的,並不是直接調的。若不采用傳內部類到方法中,也可以為外部類定義個內部類的屬性,通過該屬性調用

但這樣的調用與內部類直接訪問外部類的屬性是不同意義的。外部類和內部類的關系是:has a。那么,我們就要問問,內部類是怎么調用到外部類的屬性的?
2. 內部類是如何訪問外部類的屬性?
我們知道類的方法隱含了兩個參數:this和super。this指代的是當前對象,super指代的是父類對象,我們打印內部類中的this和super看是否指向InnerClass和Object
package onehundred; public class OuterAndInnerClass { private int number; class InnerClass{ private String name = "name"; public void getOuterField(){ System.out.println("this " + this.getClass().getName()); // this onehundred.OuterAndInnerClass$InnerClass System.out.println("super " + super.getClass().getName()); // super onehundred.OuterAndInnerClass$InnerClass System.out.println("inner class: " + number); // inner class: 0 } } public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); inner.getOuterField(); // outer.getInnerClassField(inner); // name } }
打印發現,this和super竟然都指向內部類,內部類沒有寫extends不應該默認繼承Object類嗎?為什么super是內部類本身?
還記得getClass()在哪里定義嗎?Object類中該方法被定義為final,所以this.getClass()和super.getClass()調用的是同一個方法,由於getOuterField()是InnerClass類調用,所以打印出來都是InnerClass類。正確的調用應該使用getSuperClass()
package onehundred; public class OuterAndInnerClass { private int number; class InnerClass{ private String name = "name"; public void getOuterField(){ System.out.println("this " + this.getClass().getName()); // this onehundred.OuterAndInnerClass$InnerClass System.out.println("super " + super.getClass().getName()); // super onehundred.OuterAndInnerClass$InnerClass System.out.println("super " + this.getClass().getSuperclass().getName()); // super java.lang.Object System.out.println("inner class: " + number); // inner class: 0 } } public static void main(String[] args) { OuterAndInnerClass outer = new OuterAndInnerClass(); InnerClass inner = outer.new InnerClass(); inner.getOuterField(); // outer.getInnerClassField(inner); // name } }
既然this指向當前對象,super指向父類對象,那內部類是如何調用的外部類屬性呢?
我們使用javap將代碼反編譯。
class onehundred.OuterAndInnerClass$InnerClass { final onehundred.OuterAndInnerClass this$0; onehundred.OuterAndInnerClass$InnerClass(onehundred.OuterAndInnerClass); public void getOuterField(); }
我們發現內部類多了一個外部類的final字段和一個帶參構造器,外部類的引用有了,但是是如何訪問到外部類的private字段的?
我們反編譯外部類:
public class onehundred.OuterAndInnerClass { public onehundred.OuterAndInnerClass(); public static void main(java.lang.String[]); static int access$000(onehundred.OuterAndInnerClass); }
外部類多個一個static方法,並返回一個int型的值。內部類就是通過調用這個static方法得到了外部類的private字段。如果我們在內部類中訪問外部類的boolean型字段,static方法就會返回一個boolean型的值。
總結:
可以在內部類中訪問外部類的域,因為一個方法可以引用調用這個方法的對象數據域。內部類的對象總有一個隱式引用,它指向了創建它的外部類對象。這個引用在內部類的定義中是不可見的。
外圍類的引用在內部類的構造器中設置,編譯器修改了所有內部類的構造器,添加一個外部類引用的參數。
