1. 程式人生 > >Java內部類和匿名內部類的用法詳解

Java內部類和匿名內部類的用法詳解

一、內部類:        (1)內部類的同名方法         內部類可以呼叫外部類的方法,如果內部類有同名方法必須使用”OuterClass.this.MethodName()”格式呼叫(其中OuterClass與MethodName換成實際外部類名及其方法;this為關鍵字,表示對外部類的引用);若內部類無同名方法可以直接呼叫外部類的方法。
        但外圍類無法直接呼叫內部類的private方法,外部類同樣無法直接呼叫其它類的private方法。注意:內部類直接使用外部類的方法與該方法的許可權與是否static無關,它取決於內部類是否有同名方法。

      
  1. package innerclass;
  2. public class OuterClass {
  3. private
    void outerMethod()
    {
  4. System.out.println( “It’s Method of OuterClass”);
  5. }
  6. public
    static void main(String[] args)
    {
  7. OuterClass t = new OuterClass();
  8. OuterClass.Innerclass in = t.new Innerclass();
  9. in.innerMethod();
  10. }
  11. class Innerclass {
  12. public void innerMethod() {
  13. OuterClass. this.outerMethod(); // 內部類成員方法與外部類成員方法同名時,使用this呼叫外部類的方法
  14. outerMethod(); // 內部類沒有同名方法時執行外部類的方法
  15. }
  16. private void outerMethod() {
  17. System.out.println( “It’s Method of Innerclass”);
  18. }
  19. }
  20. }
     輸出結果為:

      
  1. It’s Method of OuterClass
  2. It’s Method of Innerclass
    (2)內部類訪問外部類的變數必須宣告為final
      方法中的區域性變數,方法結束後這個變數就要釋放掉,final保證這個變數始終指向一個物件。
   首先,內部類和外部類其實是處於同一個級別,內部類不會因為定義在方法中就會隨著方法的執行完畢而跟隨者被銷燬。問題就來了,如果外部類的方法中的變數不定義final,那麼當外部類方法執行完畢的時候,這個區域性變數肯定也就被GC了,然而內部類的某個方法還沒有執行完,這個時候他所引用的外部變數已經找不到了。如果定義為final,java會將這個變數複製一份作為成員變數內置於內部類中,這樣的話,由於final所修飾的值始終無法改變,所以這個變數所指向的記憶體區域就不會變。
     注意,若使用JDK1.8,方法中內部類的方法是可以直接訪問外部類的方法的區域性變數,並且不需要宣告為final型別。

       
  1. public class OuterClass {
  2. int num1 = 0; // 成員變數
  3. private void outerMethod() {
  4. int num2 = 0; // 方法內的區域性變數
  5. class Innerclass_1 {
  6. public void innerMethod() {
  7. System.out.println(num1); // 方法中內部類的方法,可以正常訪問外部類的成員變數
  8. System.out.println(num2); // JDK1.8以前,方法中內部類的方法,不能直接訪問外部類的方法的區域性變數,必須宣告為final
  9. }
  10. }
  11. }
  12. }
      如果使用JDK1.8以前的版本,Eclipse會出現如下錯誤提示:
    (3)內部類的例項化      內部類例項化不同於普通類,普通類可以在任意需要的時候例項化,而內部類必須在外層類例項化以後方可例項化,並與外部類建立關係
     因此在外部類中的非static方法中,是可以例項化內部類物件

       
  1. private void outerMethod() {
  2. System.out.println( “It’s Method of OuterClass”);
  3. Innerclass in = new Innerclass(); //在外部類的outerMethod方法中例項化內部類是可以啊
  4. }
      但在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方法中new內部類,可以把內部類宣告為Static

      
  1. public class OuterClass {
  2. private void outerMethod() {
  3. System.out.println( “It’s Method of OuterClass”);
  4. }
  5. public static void main(String[] args) {
  6. Innerclass in = new Innerclass();
  7. in.innerMethod();
  8. }
  9. static class Innerclass { //把內部類宣告為static
  10. public void innerMethod() {
  11. System.out.println( “It’s Method of innerMethod”);
  12. }
  13. }
  14. }
     當然,一般不使用static的方式,而是推薦這種方法:x.new A() ,其中 x是外部類OuterClass的例項,A是內部類Innerclass

      
  1. package innerclass;
  2. public class OuterClass {
  3. private void outerMethod() {
  4. System.out.println( “It’s Method of OuterClass”);
  5. }
  6. public static void main(String[] args) {
  7. OuterClass.Innerclass in = new OuterClass().new Innerclass(); //使用x.new A()的方式
  8. in.innerMethod();
  9. }
  10. class Innerclass {
  11. public void innerMethod() {
  12. System.out.println( “It’s Method of innerMethod”);
  13. }
  14. }
  15. }
      x.new A() ,其中 x是外部類OuterClass的例項,A是類部類Innerclass,當然可以拆分如下,這樣就顯然很明白啦:

      
  1. public static void main(String[] args) {
  2. OuterClass out = new OuterClass(); //外部例項
  3. OuterClass.Innerclass in = out.new Innerclass(); //外部例項.new 外部類
  4. in.innerMethod();
  5. }

    (4)什麼情況下使用內部類
     典型的情況是,內部類繼承自某個類或實現某個介面,內部類的程式碼操作建立其的外層類的物件。所以你可以認為內部類提供了某種進
入其外層類的視窗。
    使用內部類最吸引人的原因是:每個內部類都能獨立地繼承自一個(介面的)實現,所以無論外層類是否已經繼承了某個(介面的)實
現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與程式設計問題就很難解決。從這個角
度看,內部類使得多重繼承的解決方案變得完整。介面解決了部分問題,而內部類有效地實現了“多重繼承”。
   (5)在靜態方法中例項化內部類例子:(內部類放在靜態方法中)

      
  1. package javatest2;
  2. public class JavaTest2 {
  3. public static void main(String[] args) {
  4. class Boy implements Person {
  5. public void say() { // 匿名內部類自定義的方法say
  6. System.out.println( “say方法呼叫”);
  7. }
  8. @Override
  9. public void speak() { // 實現介面的的方法speak
  10. System.out.println( “speak方法呼叫”);
  11. }
  12. }
  13. Person per = new Boy();
  14. per.speak(); // 可呼叫
  15. per.say(); // 不能呼叫
  16. }
  17. }
  18. interface Person {
  19. public void speak();
  20. }
       per.speak()可呼叫,而per.say()不能呼叫,這時因為per是Person物件,要想呼叫子類的方法,可以強制向下轉型為:((Boy) per).say();或者直接改為Boy per = new Boy();。 從中可發現,要想呼叫內部類的自定義的方法,必須通過內部類的物件來呼叫。那麼,匿名內部類連名字都沒有,怎麼呼叫內部類自定義的方法? (二)匿名內部類       匿名內部類也就是沒有名字的內部類正因為沒有名字,所以匿名內部類只能使用一次,它通常用來簡化程式碼編寫,但使用匿名內部類還有個前提條件:必須繼承一個父類或實現一個介面,但最多隻能繼承一個父類,或實現一個介面。
關於匿名內部類還有如下兩條規則:
    1)匿名內部類不能是抽象類,因為系統在建立匿名內部類的時候,會立即建立內部類的物件。因此不允許將匿名內部類定義成抽象類。
    2)匿名內部類不等定義構造器(構造方法),因為匿名內部類沒有類名,所以無法定義構造器,但匿名內部類可以定義例項初始化塊,
    怎樣判斷一個匿名類的存在啊?看不見名字,感覺只是父類new出一個物件而已,沒有匿名類的名字。
先看段虛擬碼

      
  1. abstract class Father(){
  2. ….
  3. }
  4. public class Test{
  5. Father f1 = new Father(){ …. } //這裡就是有個匿名內部類
  6. }
   一般來說,new 一個物件時小括號後應該是分號,也就是new出物件該語句就結束了。但是出現匿名內部類就不一樣,小括號後跟的是大括號,大括號中是該new 出物件的具體的實現方法。因為我們知道,一個抽象類是不能直接new 的,必須先有實現類了我們才能new出它的實現類。上面的虛擬碼就是表示new 的是Father的實現類,這個實現類是個匿名內部類。
    其實拆分上面的匿名內部類可為:

      
  1. class SonOne extends Father{
  2. //這裡的程式碼和上面匿名內部類,大括號中的程式碼是一樣的
  3. }
  4. public class Test{
  5. Father f1 = new SonOne() ;
  6. }
     先看一個例子,體會一下匿名內部類的用法:
    執行結果:eat something
    可以看到,我們直接將抽象類Person中的方法在大括號中實現了,這樣便可以省略一個類的書寫。並且,匿名內部類還能用於介面上

      
  1. public class JavaTest2 {
  2. public static void main(String[] args) {
  3. Person per = new Person() {
  4. public void say() { // 匿名內部類自定義的方法say
  5. System.out.println( “say方法呼叫”);
  6. }
  7. @Override
  8. public void speak() { // 實現介面的的方法speak
  9. System.out.println( “speak方法呼叫”);
  10. }
  11. };
  12. per.speak(); // 可呼叫
  13. per.say(); // 出錯,不能呼叫
  14. }
  15. }
  16. interface Person {
  17. public void speak();
  18. }
        這裡per.speak()是可以正常呼叫的,但per.say()不能呼叫,為什麼呢?注意Person per = new Person()建立的是Person的物件,而非匿名內部類的物件。其實匿名內部類連名字都沒有,你咋例項物件去呼叫它的方法呢?但繼承父類的方法和實現的方法是可以正常呼叫的,本例子中,匿名內部類實現了介面Person的speak方法,因此可以藉助Person的物件去呼叫。         若你確實想呼叫匿名內部類的自定義的方法say(),當然也有方法:       (1)類似於speak方法的使用,先在Person介面中宣告say()方法,再在匿名內部類中覆寫此方法。       (2)其實匿名內部類中隱含一個匿名物件,通過該方法可以直接呼叫say()和speak()方法;程式碼修改如下:

      
  1. public class JavaTest2 {
  2. public static void main(String[] args) {
  3. new Person() {
  4. public void say() { // 匿名內部類自定義的方法say
  5. System.out.println( “say方法呼叫”);
  6. }
  7. @Override
  8. public void speak() { // 實現介面的的方法speak
  9. System.out.println( “speak方法呼叫”);
  10. }
  11. }.say(); // 直接呼叫匿名內部類的方法
  12. }
  13. }
  14. interface Person {
  15. public void speak();
  16. }