為什么內部類可以訪問外部類的私有屬性?


使用工具:

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型的值。

總結:

  可以在內部類中訪問外部類的域,因為一個方法可以引用調用這個方法的對象數據域。內部類的對象總有一個隱式引用,它指向了創建它的外部類對象。這個引用在內部類的定義中是不可見的。

       外圍類的引用在內部類的構造器中設置,編譯器修改了所有內部類的構造器,添加一個外部類引用的參數。

      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM