1. 程式人生 > 程式設計 >詳解Java內部類——匿名內部類

詳解Java內部類——匿名內部類

  今天來看看另一個更加神奇的類——匿名內部類。

  就像它的名字表示的那樣,這個類是匿名的,用完之後,深藏功與名,就像掃地僧那樣默默潛藏於深山之中。匿名內部類不僅沒有名字,連class關鍵字都省掉了,而且匿名內部類必須繼承於某個類或者實現某個介面,長的就像這樣:

new 父類(引數列表)|實現介面() {  
     //匿名內部類的內部定義
    } 

  來看一個栗子:

public abstract class Human {
  public abstract void walk();
}

  這是一個抽象類,如果使用匿名內部類來繼承的話是這樣的:

public class AnonymousTest {
  public static void main(String[] args) {
    Human human = new Human(){
      public void walk(){
        System.out.println("AnonymousHuman can walk.");
      };
    };
    human.walk();
  }
}

  簡單粗暴,看起來就像區域性內部類的簡化版。如果不使用匿名內部類,會是怎樣呢?

  我們需要先建立一個類來繼承這抽象類:

public class Man extends Human {
  @Override
  public void walk() {
    System.out.println("Man can walk.");
  }
}

  然後再來使用這個類:

public class AnonymousTest {
  public static void main(String[] args) {
    Human human = new Man();
    human.walk();
  }
}

  因為一個單獨的類往往放在一個單獨的檔案中,如果這個類只需要建立一個物件,那未免有些大材小用了,從上面的栗子可以比較出匿名內部類的一個優勢:在類只需要建立一個物件的情況下更加簡單方便。

  再舉一個實際一點的栗子:

public class AnonymousTest {
  public static void main(String[] args) {
    Thread t = new Thread() {
      public void run() {
        for (int i = 0; i < 10; i++) {
          try {
            sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(i);
        }
      }
    };
    t.start();
  }
}

  這裡建立了一個繼承於Thread的匿名內部類,覆蓋了其中的 run方法,並建立了一個例項返回給了t,然後再呼叫run方法,可以看到,匿名內部類只能存在一個例項物件,因為new過一次就無法再建立了,也許會覺得區域性內部類已經很侷限了,為什麼要出現比區域性內部類適用範圍更小的匿名內部類?、

  這你就不懂了吧,在Java的實際使用中,匿名內部類大有用處,為什麼要使用匿名內部類呢?

  有時候,我們建立的類只需要一個例項,比如說在多執行緒中,要使用多執行緒,一般先繼承Thread類或者實現Runnable介面,然後再去呼叫它的方法,而每個任務一般都不一樣,每次都新建一個類顯然會很難管理,因為每個類只用一次就丟掉了,這個時候使用匿名內部類就很方便了,不僅不需要管理一堆一次性類,而且建立起來簡單粗暴。就像上述栗子,還能簡化成這樣:

public class AnonymousTest {
  public static void main(String[] args) {
    new Thread() {
      public void run() {
        for (int i = 0; i < 10; i++) {
          try {
            sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(i);
        }
      }
    }.start();
  }
}

  建立例項後直接呼叫run方法,簡單粗暴。

  匿名內部類不僅可以繼承於類,也可以實現於介面,比如說這樣:

public class AnonymousTest {
  public static void main(String[] args) {
    new Thread(new Runnable() {
      public void run() {
        for (int i = 0; i < 10; i++) {
          try {
            sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(i);
        }
      }
    }).start();
  }
}

  當然,還有些不得不用內部類的情況,類只能繼承於一個類,如果一個類需要使用到另一個包中的另一個類的一個protected方法,卻已經繼承於另一個類,那麼這個時候就不得不用內部類來解決了。

  比如說,還有一個Woman(女人)類:

public class Woman {
  protected void dance(){
    System.out.println("Woman can dance.");
  }
}

  這個時候,如果Man(男人)也難不住寂寞,想要dance(跳舞)一下,那該怎麼辦呢?繼承Woman類?顯然不合乎邏輯,而且也無法實現,因為已經繼承於Human類了,但就是想要dance,該怎麼辦?

  內部類的出現讓這個問題變得很簡單:

public class Man extends Human {
  @Override
  public void walk() {
    System.out.println("Man can walk.");
  }

  public void dance(){
    new Woman(){
      public void manDance(){
        super.dance();
      }
    }.manDance();
  }
}

  因為在不同的包下,不能直接使用Woman的dance方法,但是可以用內部類來繼承,從而呼叫protected方法,然後再放入Man的方法中,這樣,Man也能像Woman一樣dance了:

public class AnonymousTest {
  public static void main(String[] args) {
    Man human = new Man();
    human.walk();
    human.dance();
  }
}

  當然,使用匿名內部類還是有很多限制的:

  1、匿名內部類必須是繼承一個類或者實現一個介面,但是兩者不可兼得,同時也只能繼承一個類或者實現一個介面。

2、匿名內部類不能定義建構函式。

3、匿名內部類中不能存在任何的靜態成員變數和靜態方法。

4、匿名內部類是特殊的區域性內部類,所以區域性內部類的所有限制同樣對匿名內部類生效。

5、匿名內部類不能是抽象的,它必須要實現繼承的類或者實現的介面的所有抽象方法。

  那麼問題來了,怎樣初始化一個匿名內部類呢?畢竟匿名內部類是不能有構造器的。

  當然,首先,還是可以使用初始化塊來實現的,就像這樣:

public class AnonymousTest {
  public static void main(String[] args) {
    Human human = new Human() {
      private String name;
      {
        name = "human";
      }
      @Override
      public void walk() {
        System.out.println(name + " walk.");
      }
    };
    human.walk();
  }
}

  但是這樣顯然就比較呆板,不夠靈活,無法接受外部引數,那麼怎樣靈活使用呢?不要心急,方法總比問題多,還是有辦法解決的:

public class AnonymousTest {
  public static void main(String[] args) {
    Human human = new AnonymousTest().getHumanInstance("Frank");
    human.walk();
  }

  public Human getHumanInstance(final String name){
    return new Human() {
      private String nameA;
      {
        nameA = name;
      }
      @Override
      public void walk() {
        System.out.println(nameA + " walk.");
      }
    };
  }
}

  這裡利用初始化塊來對匿名內部類進行初始化,注意,如果匿名內部類需要使用外部的引數或者變數,那麼必須使用final修飾,因為內部類使用的其實是引數的拷貝,並不是引數本身,為了更明顯的表明引數不可變,編譯器會要求使用final關鍵字來修飾需要使用的變數。

  至此,匿名內部類講解完畢,歡迎大家繼續關注!

以上就是詳解Java內部類——匿名內部類的詳細內容,更多關於Java 匿名內部類的資料請關注我們其它相關文章!