1. 程式人生 > >關於java內部類(Inner Class) 不斷更新中

關於java內部類(Inner Class) 不斷更新中

java內部類(Inner Class) 

Inner Class 即巢狀類,也即C++和C#中的Nested Class。但Java 的Inner Class 與 C++和C#最大的不同之處在於,巢狀類包含一個指向其容器類的引用,可以訪問容器類的成員。以下程式碼演示了這一點:public class Container {
String Name;
class InnerClass
{
InnerClass(){};
public void func()
{
System.out.println(Name);
}
}
public Container(String name){ Name=name;
InnerClass a=new InnerClass();
}
public static void main(String [] arg)
{
Container a=new Container(“ContainerA");
InnerClass b=a.new InnerClass(); //注意此處
b.func();
InnerClass c=(new Container(“ContainerB")).new InnerClass();
c.func();
}
}
注意其中獨特的new語法,在靜態函式要建立一個Inner Class,必須有一個其容器類的例項。如果直接建立
InnerClass b=new InnerClass();則會導致編譯出錯。而在Container的建構函式中,建立InnerClass時,自動將this作為InnerClass的引用。在Inner Class 中使用容器類的成員,不需指定例項,自動指向建立它的容器類。 這是一個很有用的語法特徵,編譯器替我們省了許多事。 本例的輸出是: ContainerA ContainerB 還可以看到,Inner Class 可以訪問容器類的任何成員,不管是public、private或protected的成員全是透明的。反之則不然,容器類只能訪問Inner Class的public成員。

===================

匿名的內部類是沒有名字的內部類。不能extends(繼承) 其它類,但一個內部類可以作為一個介面,由另一個內部類實現。

===================

3類inner class
1、一般的inner class
class aaaa{
  class InnerClass {
    // Write Codes Here
  }
}

2、Method local Inner Class
local class 是定義在method內的class,其 scope 在該method 內

3、Anonymous Inner Class
 一種形式:return new Destination{ //inner class };
另一種形式:someMethod(new SomeClass( ) { //code } );
[注意]
(1)anonymous nested class 必須implement 某個 interface 或是 extend 某個 class,但是不使用         implements 或 extends 關鍵字
(2)anonymous class 內不能宣告 constructor
(3)可宣告 instance initializer 做初值設定

==================

class Out{

class Inner1{}

static class Inner2{}

Inner1 需要通過 Out 的例項構造。

Out out = new Out();
Out.Inner1 oi1 = out.new Inner1();

Inner2 可以通過 Out 的類名構造。

Out.Inner2 oi2 = new Out.Inner2();

例如上面的例項:新增靜態內部類
static class Inner2{
   public void f2(){System.out.println("pppp");}
  }
main函式的呼叫時用Container類:Container.Inner2 i2 = new Container.Inner2();   i2.f2();

===============

總結:當前的外部類中,內部類的public或者private屬性可以被訪問
class InnerClass
 {
  private int x = 5;
  public int y = 10;
 InnerClass(){};
 public void func()
 {
 System.out.println(Name+"-x-"+x+"-y-"+y);
 }
 }
 class Inner1{
  public void f1(){
   InnerClass ic = new InnerClass();
   System.out.println(ic.y+"|||"+ic.x);
   System.out.println(Name);
   }
 }

============================

1. 巢狀類

首先必須要注意,內部類和巢狀類的區別。巢狀類擁有訪問宿主類任意成員的許可權。巢狀類除了訪問許可權外,並不會獲得宿主類例項的引用,也不會隨宿主類自動產生例項。

C#

class Class1
{
  private int x = 13;

  public void Test()
  {
    new Class1Sub(this).Test();
  }

  public class Class1Sub
  {
    private Class1 o;

    public Class1Sub(Class1 o)
    {
      this.o = o;
    }

    public void Test()
    {
      Console.WriteLine(o.x);
    }
  }
}

class Program
{
  static void Main()
  {
    // 1.
    new Class1().Test();

    // 2.
    Class1 o = new Class1();
    Class1.Class1Sub s = new Class1.Class1Sub(o);
    s.Test();
  }
}


Java

class Class1 {
  private int x = 13;
  
  public void test() {
    new Class1Sub(this).test();
  }
  
  public static class Class1Sub {
    private Class1 o;
    
    public Class1Sub(Class1 o){
      this.o = o;
    }
    
    public void test() {
      System.out.println(o.x);
    }
  }
}

public class program {
  public static void main(String[] args) {
    // 1.
    new Class1().test();
    
    // 2.
    Class1 o = new Class1();
    Class1.Class1Sub s = new Class1.Class1Sub(o);
    s.test();
  }
}


Java 的例子和 C# 基本類似,不過要注意的是 Java Class1.Class1Sub 被申明為 "static class"。
另外,C# 巢狀類預設是 private 方式的,也就是說不能被外界訪問。可以被賦予 private/internal/protected/internal protected/public中的任一種許可權。Java 的巢狀類預設是 friendly 方式,可以被賦予 private/protected/public/friendly中的一種。

2. 內部類

內部類和巢狀類比較相似,但沒有static修飾,不能像巢狀類那樣在外部建立內部類的例項物件。它雖然不會自動隨宿主類生成例項,但卻能自動獲取宿主類的引用,從而訪問宿主類的例項成員。

我們修改上面的例子,將其改成內部類,我們發現Class1Sub可以直接訪問宿主例項的任意欄位,並不需要使用宿主引用。這其實是Java編譯器做的一些“魔法”,當在宿主例項內生成內部類時會自動將宿主引用隱性傳遞給內部類例項。當然,在內部類也可以使用"宿主名.this"獲取宿主引用。

class Class1 {
  private int x = 13;
  
  public void test() {
    new Class1Sub().test();
  }
  
  class Class1Sub {  
    public void test() {
      System.out.println(Class1.this.toString());
      System.out.println(x);
    }
  }
}

public class program {
  public static void main(String[] args) {
    new Class1().test();
  }
}


內部類可以跨越多重巢狀,訪問任意級別的巢狀類成員。

class Class1 {
  void doSomething(){};
  
  class Class1Sub {
    class class1Sub2 {
      void test() { doSomething(); }
    }
  }
}


3. 本地內部類

我們還可以在方法內部建立內部類,這種內部類被稱之為“本地內部類(local inner class)”。本地內部類不能有許可權修飾符,和下面的匿名內部類非常相似。如果本地內部類需要使用方法引數,則必須為該引數新增final關鍵字。

class Class1 {
  private int x = 13;
  
  void test() {
    class Sub {
      Sub() { System.out.println(x); }
    }
    
    new Sub();
  }
  
  void test2(final String s) {
    class Sub {
      Sub() { System.out.println(s); }
    }
    
    new Sub();    
  }
}

public class program {
  public static void main(String[] args) {
    new Class1().test();
    new Class1().test2("abc");
  }
}


3. 匿名內部類

我們可以在方法內部建立一個實現某個介面或者繼承某個類的匿名類,它具備內部類的全部功能,只不過因為沒有“名字”,我們不能在外面例項化它,也不能建立構造方法,而只能使用例項初始化程式碼段。如果匿名類需要使用外部方法引數,必須為該引數加上final關鍵字。

abstract class Base {
  abstract void test();
}

interface IBase {
  void test();
}

class Class1 {
  private int x = 13;
  
  public Base makeBase() {
    return new Base(){
      {
        System.out.println("new Base...");
      }
      
      public void test() {
        System.out.println(x);
      }
    };
  }
  
  public IBase makeIBase(final String s) {
    return new IBase(){
      public void test() {
        System.out.println(s);
      }
    };
  }
}

public class program {
  public static void main(String[] args) {
    new Class1().makeBase().test();
    new Class1().makeIBase("abc").test();
  }
}


一般情況下,匿名內部類和本地內部類都可以實現相同的功能。選擇本地內部類的額外理由是:

1. 需要或者覆寫構造方法。
2. 建立多個本地內部類例項。
3. 沒有可用的基類或介面。(匿名內部類必須實現某個介面和繼承自某個基類。)

匿名內部類和本地內部類還可以放在任意的程式碼範疇(scope)內。

interface I1 {}

class ClassX
{
  void print(final int i) {
    if (i > 0) {
      class Sub {
        Sub() { System.out.println(i); }
      }
      
      new Sub();
    }
    else {
      new I1() {
        {
          System.out.println("i <= 0");
        }
      };
    }
  }
}

public class program {
  public static void main(String[] args) {
    new ClassX().print(1);
    new ClassX().print(0);
  }
}


4. 繼承內部類

在宿主類內部繼承內部類,和正常情況下沒什麼區別。

class Class1 {
  
  private int x = 10;
  public Class1() { new Sub2(); }
  
  class Sub {
    Sub(){ System.out.println("Sub:" + x); }
  }
  
  class Sub2 extends Sub {
    Sub2(){ System.out.println("Sub2:" + x); }
  }
}


而為了在外部繼承內部類,就必須:
1. 包含有宿主類引用引數的構造方法。
2. 在構造方法內部呼叫 super() 方法。

class Class1 {
  class Class1Sub {
  }
}

class Class1SubX extends Class1.Class1Sub {
  public Class1SubX(Class1 o) {
    o.super();
  }
}

public class program {
  public static void main(String[] args) {
    new Class1SubX(new Class1());
  }
}


雖然在外部繼承內部類以後,我們可以在外部建立內部類例項,但同時也失去了隱性訪問宿主類成員的權利。

5. 覆寫內部類

繼承含有內部類的宿主類,並不會同時繼承其內部類,即便在繼承類內部有相同名稱的內部類,也完全是兩個不同的型別。

class Class1 {
  void test() { new Sub(); }
  
  class Sub {
    Sub() { System.out.println("Class1.Sub"); }
  }
}

class Class2 extends Class1 {

  void test() { new Sub(); }
  
  class Sub {
    Sub() { System.out.println("Class2.Sub"); }
  }  
}

public class program {
  public static void main(String[] args) {
    Class1 o = new Class2();
    o.test();
  }
}


輸出
Class2.Sub

由於編譯器並不會因為 Class2.Sub 是 Class1.Sub 的繼承類,因此並沒有自動呼叫 Class1.Sub 構造方法。我們修改一下,使得 Class2.Sub extends Class1.Sub,OK!這回對了。

class Class1 {
  void test() { new Sub(); }
  
  class Sub {
    Sub() { System.out.println("Class1.Sub"); }
  }
}

class Class2 extends Class1 {

  void test() { new Sub(); }
  
  class Sub extends Class1.Sub {
    Sub() { System.out.println("Class2.Sub"); }
  }  
}

public class program {
  public static void main(String[] args) {
    Class1 o = new Class2();
    o.test();
  }
}


輸出:
Class1.Sub
Class2.Sub