Java編程思想——細話Java內部類(下)

一、匿名內部類

      像下邊這樣雖然返回的是B的對象,但是其後卻帶有B的{},括號中可以是類的成員和方法。這種類的形式由於對外是隱藏的,且沒有直接的類名,所以稱爲匿名內部類。

public class A{
    public B getB(){
       return new B(){};
    }
}

這裏需要注意的一點是,如果在匿名內部類中使用外部定義的對象,那麼編譯器要求其參數引用是final的(如果不使用就不需要)。

public class A{
    public B getB(final String name){
       return new B(){
           private String st=name;//name必須是final
           public String getStr(){
               return st;
           }
       };
    }
}

因爲匿名內部類是沒有名字的,所以它是不可能有命名構造器的,但是可以通過實例初始化達到爲匿名內部類創建一個構造器的效果。

interface A{
   public void f();
}
public class UseA{
   public static A getA(final int i){
      //因內部類要使用i,所以應爲final
      return new A(){
       public void f(){
          System.out.print("this num="+i);
       }
     };//通過實例初始化創建構造器效果
   }
   public static void main(String[] args){
      A a=getA(10);
      a.f();
   }
}
out:
this num=10

但是要注意的是這種方式創建的構造器有一個限制——實力初始化方法不能被重載,所以只能有一個這樣的構造器。

注:匿名內部類與正規的繼承相比有些受限,因爲匿名內部類既可以擴展類,也可以實現接口,但是不能倆者兼備。而且如果實現接口,也只能實現一個接口。

看一下反編譯後內部類的效果

final class UseA$1 implements A
{
  UseA$1(int paramInt) {}
  
  public void f()
  {
    System.out.print("this num=" + this.val$i);
  }
}

二、嵌套類

      帶有static的內部類被稱爲嵌套類(要知道普通的內部類對象隱式地保存了一個引用,指向創建它的外圍對象)。而此時的內部類會有倆個特點:

  • 創建嵌套類對象不需要外圍類的對象。
  • 不能從嵌套類的對象中訪問非靜態的外圍類對象。

注意:嵌套類沒有this引用。

嵌套類可以作爲接口(接口中除了一些聲明外本不能存放任何代碼)的一部分,放到接口中的任何類自動地是public和static,且可以在內部類中實現外部接口。

public interface A{
   void f();
   class B implements A{
      public void f(){
         System.out.print("Hello!");
      }
      public static void main(String[] args){
        new B().f();//可以直接通過new進行對象創建(static讓其變成了獨立的公共類)
     }
   }
}

上邊的例子經過編譯不會出現任何問題,但是在執行時會報錯:


原因:

如果在執行時直接使用"java A",那當然會報錯,因爲嵌套類本就不屬於接口A(所以此時接口A中實際上並沒有其他代碼),看一下編譯後嵌套類的效果:

public class A$B implements A
{
  public void f()
  {
    System.out.print("Hello!");
  }
  
  public static void main(String[] paramArrayOfString)
  {
    new B().f();
  }
}
很顯然main方法在A$B類(B類此時在接口A的命名空間中)中,所以要使用”java A$B“來執行代碼,此時A$B實際是一個獨立的類(Linux中需要將$進行轉義)。

三、爲什麼要使用內部類?

       一般來說內部類繼承自某個類或者實現某個接口,內部類的代碼操作創建它的外圍類的對象。所以可以認爲內部類提供了某種進入其外圍類的窗口。

*最吸引人的原因:

     每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。

解釋:

     也就是對於一個普通類來說在不考慮內部類來說,要實現多繼承機制只能通過”繼承一個類+實現多個接口“或”只實現多個接口“。但是如果沒有接口而只有類或抽象類呢?那要想實現多繼承就只能通過內部類實現,見如下實例:

class B{
	void show(){
		System.out.println("This is B!");
	}
}
abstract  class C{
	public int i=10;
	abstract void abShow();
	public void showI(){}
}
public class  A extends B{  //主類繼承B
    class Inner extends C{  //內部類繼承C
		public void abShow(){
		    System.out.println("This is C!");
		}
		C makeC(){
			return new C(){ //匿名內部類繼承C並重寫C中方法
			   public void abShow(){
			   System.out.println("This is makeC!");
		        }
			   public void showI(){
			     System.out.println("i="+i);
		        }
			};
		}
	}

	public static void main(String[] args){
		A a=new A();
		A.Inner in=a.new Inner();
		C c=in.makeC();
		a.show();
		in.abShow();
		c.abShow();
		c.showI();

	}
}
out:
This is B!
This is C!
This is makeC!
i=10

*內部類的一些特性:

  • 內部類可以有多個實例,每個實例都有自己的狀態信息,並且與其外圍類對象的信息相互獨立。
  • 在單個外圍類中,可以讓多個內部類以不同的方式實現同一個接口,或繼承同一個類(就如上邊的例子)。
  • 創建內部類對象的時刻並不依賴於外圍類對象的創建。
  • 內部類並沒有令人迷惑的”is-a“關係;它就是一個獨立的實體。

*閉包與回調(callback):

  • 閉包——是一個可調用的對象,它記錄了一些信息,這些信息來自於創建它的作用域。
  • 得出結論——內部類就是一個面向對象的閉包(不僅包含外圍類對象的信息還自動有一個指向此外圍類對象的引用)。
  • 回調——通過回調,對象可以攜帶一些信息,這些信息允許它在稍後的某個時刻調用初始化的對象。它的價值在於它的靈活性,可以在運行時動態地決定需要調用什麼方法。

*內部類在控制框架中的使用:

控制框架由一個或一組類構成,用以將變化的事物與不變的事物相互分離。

  • 控制框架的完整實現是由單個的類創建的,從而使得實現的細節被封裝。內部類用來表示解決問題所必需的各種不同的action()。
  • 內部類可以很容易地訪問外圍類的任意成員,所以可以避免這種實現變得笨拙。如果沒有這種能力,代碼將變得令人討厭,以至於你肯定會選擇別的方法。

使用內部類可以在單一的類中產生對同一個基類Event的多種導出版本。

*內部類標識符:

前邊的反編譯.class文件中顯示了每個對象的全部信息,內部類在編譯時同樣會生成一個獨立的.class文件。可以看出這些文件的命名有嚴格的規則:外圍類的名字,加上”$“,再加上內部類的名字就是一個內部類的.class文件名:

class A$Inner$1  extends C
{
  A$Inner$1(A.Inner paramInner) {}
  
  public void abShow()
  {
    System.out.println("This is makeC!");
  }
  
  public void showI()
  {
    System.out.println("i=" + this.i);
  }
}
在名字中可以看到一個數字”1“,這個代表匿名內部類,由於匿名內部類沒有名字,所以編譯器會簡單的產生一個數字作爲其標識符。如果內部類是嵌套在別的內部類中,需要在最內層的類名前再加一個”$“。

----------------------------------------------------------------------------------------------

       接口和內部類的產生使得Java的多繼承機制更加完善,尤其對於內部類來說,其允許繼承多個非接口類型(類或抽象類),並且比C++中多繼承更好理解和使用。但是在實際開發中什麼時候應該用這些技術,應該在設計階段考慮。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章