天天看點

Java學習筆記一面向對象内部類

内部類成員可以直接通路外部類的私有資料,外部類不能通路内部類的實作細節,例如内部類的成員變量。内部類比外部類可以多使用三個修飾符:private、protected、static。内部類的上一級單元是外部類,它就具有4個作用域:同一個類、同一個包、父子類和任何位置。

非靜态内部類

沒有使用static修飾額成員内部類是非靜态内部類。在非靜态内部類對象裡,保持了一個它所寄生的外部類對象的引用(當調用非靜态内部類的執行個體方法時,必須有一個非靜态内部類執行個體,非靜态内部類執行個體必須寄生在外部類執行個體裡)。

public class DiscernVariable
{
  private String prop = "外部類的執行個體變量";
  private class InClass
  {
    private String prop = "内部類的執行個體變量";
    public void info()
    {
      String prop = "局部變量";
      //通過外部類類名.this.varName通路外部類執行個體變量
      System.out.println("外部類的執行個體變量值:"+DiscernVariable.this.prop);
      //通過this.varName通路内部類執行個體變量
      System.out.println("内部類的執行個體變量值:"+this.prop);
      //直接通路局部變量
      System.out.println("局部變量值:"+prop);
    }
  }
  public void test()
  {
    InClass in = new InClass();
    in.info();
  }
  public static void main(String[] args)
  {
    new DiscernVariable().test();
  }
}      

非靜态内部類的成員隻在非靜态内部類範圍是可知的,并不能被外部類直接使用。如果外部類需要通路非靜态内部類的成員,則必須顯示建立非靜态内部類對象來調用通路其執行個體成員。–> 非靜态内部類對象必須寄生在外部類對象裡,而外部類對象則不必一定有非靜态内部類對象寄生其中。簡單地說,如果存在一個非靜态内部類對象,則一定存在一個被它寄生的外部類對象。但外部類對象存在時,外部類對象裡不一定寄生了非靜态内部類對象。是以外部類對象通路非靜态内部類成員時,可能非靜态普通内部類對象根本不存在。而非靜态内部類對象通路外部類成員時,外部類對象一定存在。

不允許外部類的靜态成員中直接使用非靜态内部類。非靜态内部類不能擁有靜态成員。

public class StaticTest
{
  private class In{}
  public static void main(String[] args)
  {
    new In(); // 引發編譯異常,無法通路非靜态成員In類
  }
}      

在外部類以外使用非靜态内部類:内部類不能使用private通路控制權限,private修飾的内部類隻能在外部類内部使用。對于使用其他通路控制符修飾的内部類,則能在通路控制符對應的通路權限内使用。

在外部類以外定義内部類變量的文法:​​

​OuterClass.InnerClass varName​

​​。

非靜态内部類的對象必須寄生在外部類對象裡,建立非靜态内部類對象前,必須建立外部類對象。在外部類外建立非靜态内部類執行個體文法:​​

​OuterInstance.new InnerConstructor()​

​​。

舉例:

class Out
{
  class In
  {
    public In(String msg)
    {
      System.out.println(msg);
    }
  }
  public class CreateInnerInstance
  {
    public static void main(String[] args)
    {
      Out.In in = new Out().new In("測試資訊");
      // 等同于下面的
      Out.In in;
      Out out = new Out();
      in = out.new in("測試資訊");
    }
  }
}      

建立非靜态内部類的子類,必須保證讓子類構造器可以調用非靜态内部類的構造器,調用非靜态内部類的構造器時,必須存在一個外部類對象。

public class SubClass extends Out.In
{
  public SubClass(Out out)
  {
    //通過傳入的Out對象顯式調用In的構造器
    out.super("hello");
  }
}      

非靜态内部類In類的構造器必須使用外部類對象來調用,代碼中super代表調用In類的構造器,而out代表外部類對象。SubClass是非靜态内部類In類的子類,非靜态内部類In對象裡有一個對Out對象的引用,其子類也應該持有對Out對象的引用。當建立SubClass對象時傳給該構造器的Out對象,就是SubClass對象裡Out對象引用所指向的對象。

靜态内部類

static修飾的内部類屬于外部類本身不屬于外部類的對象。靜态内部類可以包含靜态成員,也可以包含非靜态成員。根據靜态成員不能通路非靜态成員的規則,靜态内部類不能通路外部類的執行個體成員,隻能通路外部類的類成員。即使是靜态内部類的執行個體方法也不能通路外部類的執行個體成員,隻能通路外部類的靜态成員。

靜态内部類對象不是寄生在外部類的執行個體中,而是寄生在外部類的類本身中。當靜态内部類對象存在時,并不存在一個被它寄生的外部類對象,靜态内部類對象隻持有外部類的類引用,沒有持有外部類對象的引用。如果允許靜态内部類的執行個體方法通路外部類的執行個體成員,但找不到被寄生的外部類對象,這将引起錯誤。

public class StaticInnerClassTest
{
  private int prop1 = 5;
  private static int prop2 = 9;
  static class StaticInnerClass
  {
    //靜态内部類裡可以包含靜态成員
    private static int age;
    public void accessOuterProp()
    {
      //靜态内部類無法通路外部類的執行個體
      System.out.println(prop1);
      //可以通路通路外部類的靜态成員
      System.out.println(prop2);
    }
  }
}      

靜态内部類是外部類的一個靜态成員,是以外部類的所有方法、所有初始化塊中可以使用靜态内部類來定義變量、建立對象等。外部類依然不能直接通路靜态内部類的成員,但可以使用靜态内部類的類名作為調用者來通路靜态内部類的類成員,也可以使用靜态内部類對象作為調用者來通路靜态内部類的執行個體成員。

public class AccessStaticInnerClass
{
  static class StaticInnerClass
  {
    private static int prop1 = 5;
    private int prop2 = 9;
  }
  public void accessInnerProp()
  {
    //通過類名通路靜态内部類的類成員
    System.out.println(StaticInnerClass.prop1);
    //通過執行個體通路靜态内部類的執行個體成員
    System.out.println(new StaticInnerClass().prop2);
  }
}      

在外部類以外定義靜态内部類變量的文法:​

​OuterClass.InnerClass varName​

​​。

建立靜态内部類對象時無須建立外部類對象,在外部類外建立靜态内部類執行個體的文法:​​

​new OuterClass.InnerConstructor()​

class StaticOut
{
  static class StaticIn
  {
    public StaticIn()
    {
      System.out.println("靜态内部類的構造器");
    }
  }
}
public class CreateStaticInnerInstance
{
  public static void main(String[] args)
  {
    StaticOut.StaticIn in = new StaticOut.StaticIn();
    // 等同下面的代碼
    StaticOut.StaticIn in;
    in = new StaticOut.StaticIn();
  }
}      

建立靜态内部類的子類:​

​public class StaticSubClass extends StaticOut.StaticIn {}​

局部内部類

如果把一個内部類放在方法裡定義,則這個内部類就是一個局部内部類,局部内部類僅在該方法裡有效。局部内部類不能使用通路控制符和static修飾符。使用局部類定義變量、建立執行個體或派生子類,都隻能在局部内部類所在的方法内進行。

匿名内部類

匿名内部類适合建立隻需要一次使用的類,建立匿名内部類時會立即建立一個該類的執行個體,這個類定義立即消失,匿名内部類不能重複使用。匿名内部類必須繼承一個父類,或實作一個接口。匿名類不能是抽象類,無法建立該匿名内部類對象。匿名内部類不能定義構造器,匿名内部類沒有類名,無法定義構造器。但是匿名内部類可以定義初始化塊,可以通過執行個體初始化塊來完成構造器需要完成的事情。

new 實作接口() | 父類構造器(實參清單)
{
}      

舉例

interface Product
{
  public double getPrice();
  public String getName();
}
public class AnonymousTest
{
  public void test(Product p){
    System.out.println("購買一個"+p.getName()+",花掉"+p.getPrice());
  }
  public static void main(String[] args)
  {
    AnonymousTest ta = new AnonymousTest();
    ta.test(new Product()
    {
      public double getPrice() { return 567.8; }
      public String getName() { return "AGP顯示卡"; }
    });
  }
}      

當通過實作接口來建立匿名内部類時,匿名内部類不能顯示建立構造器,因為匿名内部類隻有一個隐式的無參數構造器,故new接口名後的括号裡不能傳入參數值。

abstract class Device
{
  private String name;
  public abstract double getPrice();
  public Device() {}
  public Device(String name) { this.name = name; }
}
public class AnonymousTest
{
  public void test(Device p){
    System.out.println("購買一個"+p.getName()+",花掉"+p.getPrice());
  }
  public static void main(String[] args)
  {
    AnonymousTest ta = new AnonymousTest();
    //調用有參數的構造器建立Device匿名實作類的對象
    ta.test(new Device("電子示波器")
    {
      public double getPrice() { return 567.8; }
    });
    //調用無參數的構造器建立Device匿名實作類的對象
    Device d = new Device()
    {
      //初始化塊
      { System.out.println("匿名内部類的初始化塊"); }
      //實作抽象方法
      public double getPrice() { return 567.8; }
      //重寫父類的執行個體方法
      public String getName() { return "顯示卡"; }
    });
    ta.test(d);
  }
}