天天看點

Java内部類和匿名内部類的用法

Java内部類和匿名内部類的用法

一、内部類: 

      (1)内部類的同名方法

        内部類可以調用外部類的方法,如果内部類有同名方法必須使用"OuterClass.this.MethodName()"格式調用(其中OuterClass與MethodName換成實際外部類名及其方法;this為關鍵字,表示對外部類的引用);若内部類無同名方法可以直接調用外部類的方法。

        但外圍類無法直接調用内部類的private方法,外部類同樣無法直接調用其它類的private方法。注意:内部類直接使用外部類的方法與該方法的權限與是否static無關,它取決于内部類是否有同名方法。

package innerclass;
public class OuterClass {
  private void outerMethod() {
    System.out.println("It's Method of OuterClass");
  }
  public static void main(String[] args) {
    OuterClass t = new OuterClass();
    OuterClass.Innerclass in = t.new Innerclass();
    in.innerMethod();
  }

  class Innerclass {
    public void innerMethod() {
       OuterClass.this.outerMethod();// 内部類成員方法與外部類成員方法同名時,使用this調用外部類的方法
       outerMethod();// 内部類沒有同名方法時執行外部類的方法
    }
    private void outerMethod() {
      System.out.println("It's Method of Innerclass");
    }
  }
}      

     輸出結果為:

It's Method of OuterClass
It's Method of Innerclass      

    (2)内部類通路外部類的變量必須聲明為final

      方法中的局部變量,方法結束後這個變量就要釋放掉,final保證這個變量始終指向一個對象。

   首先,内部類和外部類其實是處于同一個級别,内部類不會因為定義在方法中就會随着方法的執行完畢而跟随者被銷毀。問題就來了,如果外部類的方法中的變量不定義final,那麼當外部類方法執行完畢的時候,這個局部變量肯定也就被GC了,然而内部類的某個方法還沒有執行完,這個時候他所引用的外部變量已經找不到了。如果定義為final,java會将這個變量複制一份作為成員變量内置于内部類中,這樣的話,由于final所修飾的值始終無法改變,是以這個變量所指向的記憶體區域就不會變。

     注意,若使用JDK1.8,方法中内部類的方法是可以直接通路外部類的方法的局部變量,并且不需要聲明為final類型。

public class OuterClass {
  int num1 = 0;// 成員變量

  private void outerMethod() {
    int num2 = 0;// 方法内的局部變量
    class Innerclass_1 {
      public void innerMethod() {
        System.out.println(num1);// 方法中内部類的方法,可以正常通路外部類的成員變量
        System.out.println(num2);// JDK1.8以前,方法中内部類的方法,不能直接通路外部類的方法的局部變量,必須聲明為final
      }
    }
  }
}      

      如果使用JDK1.8以前的版本,Eclipse會出現如下錯誤提示:

Java内部類和匿名内部類的用法

    (3)内部類的執行個體化

     内部類執行個體化不同于普通類,普通類可以在任意需要的時候執行個體化,而内部類必須在外層類執行個體化以後方可執行個體化,并與外部類建立關系

 是以在外部類中的非static方法中,是可以執行個體化内部類對象

private void outerMethod() {
    System.out.println("It's Method of OuterClass");
    Innerclass in = new Innerclass();//在外部類的outerMethod方法中執行個體化内部類是可以啊
  }      

但在static方法中,就要注意啦!!!!

不能在static方法中直接new内部類,否則出現錯誤:

     No enclosing instance of type OuterClass is accessible. Must qualify the allocation with an enclosing instance of type OuterClass (e.g. x.new A() where x is an instance of OuterClass).

     這是因為靜态方法是在類執行個體化之前就可以使用的,通過類名調用,這時動态内部類都還沒執行個體化呢,怎麼用,總不能調用一個不存在的東西吧。

Static

public class OuterClass {
  private void outerMethod() {
    System.out.println("It's Method of OuterClass");
  }

  public static void main(String[] args) {
    Innerclass in = new Innerclass();
    in.innerMethod();
  }

  static class Innerclass {//把内部類聲明為static
    public void innerMethod() {
      System.out.println("It's Method of innerMethod");

    }
  }

}      
package innerclass;
public class OuterClass {
  private void outerMethod() {
    System.out.println("It's Method of OuterClass");
  }
  public static void main(String[] args) {
    OuterClass.Innerclass in = new OuterClass().new Innerclass();//使用x.new A()的方式
    in.innerMethod();
  }
  class Innerclass {
    public void innerMethod() {
      System.out.println("It's Method of innerMethod");
    }
  }
}      

x.new A() ,其中 x是外部類OuterClass的執行個體,A是類部類Innerclass,當然可以拆分如下,這樣就顯然很明白啦:

public static void main(String[] args) {
    OuterClass out = new OuterClass();//外部執行個體
    OuterClass.Innerclass in = out.new Innerclass();//外部執行個體.new 外部類
    in.innerMethod();
  }      

    (4)什麼情況下使用内部類

     典型的情況是,内部類繼承自某個類或實作某個接口,内部類的代碼操作建立其的外層類的對象。是以你可以認為内部類提供了某種進

入其外層類的視窗。

    使用内部類最吸引人的原因是:每個内部類都能獨立地繼承自一個(接口的)實作,是以無論外層類是否已經繼承了某個(接口的)實

現,對于内部類都沒有影響。如果沒有内部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與程式設計問題就很難解決。從這個角

度看,内部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而内部類有效地實作了“多重繼承”。

   (5)在靜态方法中執行個體化内部類例子:(内部類放在靜态方法中)

package javatest2;
public class JavaTest2 {
  public static void main(String[] args) {
    class Boy implements Person {
      public void say() {// 匿名内部類自定義的方法say
        System.out.println("say方法調用");
      }
      @Override
      public void speak() {// 實作接口的的方法speak
        System.out.println("speak方法調用");
      }
    }
    Person per = new Boy();
    per.speak();// 可調用
    per.say();// 不能調用
  }
}
interface Person {
  public void speak();
}      

       per.speak()可調用,而per.say()不能調用,這時因為per是Person對象,要想調用子類的方法,可以強制向下轉型為:((Boy) per).say();或者直接改為Boy per = new Boy();。

從中可發現,要想調用内部類的自定義的方法,必須通過内部類的對象來調用。那麼,匿名内部類連名字都沒有,怎麼調用内部類自定義的方法?

(二)匿名内部類

      匿名内部類也就是沒有名字的内部類正因為沒有名字,是以匿名内部類隻能使用一次,它通常用來簡化代碼編寫,但使用匿名内部類還有個前提條件:必須繼承一個父類或實作一個接口,但最多隻能繼承一個父類,或實作一個接口。

關于匿名内部類還有如下兩條規則:

    1)匿名内部類不能是抽象類,因為系統在建立匿名内部類的時候,會立即建立内部類的對象。是以不允許将匿名内部類定義成抽象類。

    2)匿名内部類不等定義構造器(構造方法),因為匿名内部類沒有類名,是以無法定義構造器,但匿名内部類可以定義執行個體初始化塊,

    怎樣判斷一個匿名類的存在啊?看不見名字,感覺隻是父類new出一個對象而已,沒有匿名類的名字。

先看段僞代碼

abstract class Father(){
....
}
public class Test{
   Father f1 = new Father(){ .... }  //這裡就是有個匿名内部類
}      

   一般來說,new 一個對象時小括号後應該是分号,也就是new出對象該語句就結束了。但是出現匿名内部類就不一樣,小括号後跟的是大括号,大括号中是該new 出對象的具體的實作方法。因為我們知道,一個抽象類是不能直接new 的,必須先有實作類了我們才能new出它的實作類。上面的僞代碼就是表示new 的是Father的實作類,這個實作類是個匿名内部類。

    其實拆分上面的匿名内部類可為:

class SonOne extends Father{
  ...       //這裡的代碼和上面匿名内部類,大括号中的代碼是一樣的
}
public class Test{
   Father f1 = new SonOne() ;
}      

     先看一個例子,體會一下匿名内部類的用法:

Java内部類和匿名内部類的用法

    運作結果:eat something

    可以看到,我們直接将抽象類Person中的方法在大括号中實作了,這樣便可以省略一個類的書寫。并且,匿名内部類還能用于接口上

public class JavaTest2 {
  public static void main(String[] args) {
    Person per = new Person() {
      public void say() {// 匿名内部類自定義的方法say
        System.out.println("say方法調用");
      }
      @Override
      public void speak() {// 實作接口的的方法speak
        System.out.println("speak方法調用");
      }
    };
    per.speak();// 可調用
    per.say();// 出錯,不能調用
  }
}

interface Person {
  public void speak();
}      

        若你确實想調用匿名内部類的自定義的方法say(),當然也有方法:

      (1)類似于speak方法的使用,先在Person接口中聲明say()方法,再在匿名内部類中覆寫此方法。

      (2)其實匿名内部類中隐含一個匿名對象,通過該方法可以直接調用say()和speak()方法;代碼修改如下:

public class JavaTest2 {
  public static void main(String[] args) {
    new Person() {
      public void say() {// 匿名内部類自定義的方法say
        System.out.println("say方法調用");
      }

      @Override
      public void speak() {// 實作接口的的方法speak
        System.out.println("speak方法調用");
      }
    }.say();// 直接調用匿名内部類的方法
  }
}
interface Person {
  public void speak();
}      

繼續閱讀