只想把基礎(chǔ)打好-Java內(nèi)部類

內(nèi)部類的類型

普通內(nèi)部類(非靜態(tài))

public class Outer {
    class Inner{}
}

實際例子:

public interface Iterator<T> {
    boolean hasNext();
    T next();
}
public class MyList<T> {
   private Object[] list;
   private int size;
   private int currentSize;

   public MyList(int size){
       list=new Object[size];
       this.size=size;
   }
   public void add(T element){
       if(currentSize<size){
           list[currentSize++]=element;
       }else{
           //拋出異常
       }
   }
   public void remove(){
       if(currentSize>0){
           list[--currentSize]=null;
       }else{
           //拋出異常
       }
   }
   Iterator interator(){
       return new Itr<T>();
   }

class Itr<T> implements Iterator<T>{
    int cursor;       // index of next element to return

    @Override
    public boolean hasNext() {
        return cursor<currentSize;
    }

    @Override
    public T next() {
        if(hasNext()){
          //  lastRet++;
            int i=cursor;
            cursor++;
            return  (T)list[i];

        }

        return null;
    }
}
}

測試代碼

  public static void main(String[] args){
        MyList<String> list=new MyList<>(10);
        list.add("today");
        list.add("is");
        list.add("week");
        list.add("?");
        Iterator iterator=list.interator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }

運行結(jié)果:

today
is
week
?

這是一個典型的迭代器模式。從上面例子可以看出,內(nèi)部可以訪問外圍類的成員變量,當然也可以訪問成員方法,因為生成內(nèi)部類的對象的時候就與制造它的外圍類對象就有了聯(lián)系,內(nèi)部類有外圍類的所有元素的訪問權(quán)。這是怎么做到的呢?

  protected void test(){
        Outer outer=new Outer();
       // Outer.Inner inner=new Outer().new Inner();
        Outer.Inner inner=outer.new Inner();
        inner.getI();
    }

    class Outer{
        int i=10;
        class Inner{
            public void getI(){
                System.out.println("outer class i="+i);
            }
        }
    }

從上面看出得到一個內(nèi)部實例的例子,創(chuàng)建一個內(nèi)部類實例的時候必須要得到一個外圍類的實例,然后再按照outer.new Inner()來得到一個內(nèi)部類對象。擁有外部類對象之前是不可能創(chuàng)建內(nèi)部類實例的(除非是靜態(tài)內(nèi)部類,稍后說)。當通過外圍類對象創(chuàng)建內(nèi)部類實例的時候,秘密捕獲了這個指向外圍類的對象,當在內(nèi)部類訪問外圍類的成員的就是用的這個對象調(diào)用外圍的對象。幸運的是,編輯器幫你處理了這些細節(jié)。

public void getI(){
        //完整調(diào)用語句
               System.out.println("outer class i="+Outer.this.i);
           }

在java中比較常見的是集合類中使用迭代器。

局部內(nèi)部類

public class Outer {
    public void test(){
        class Inner{}
        new Inner();
    }
  //  class Inner{}
}

可以在任意的方法或作用域定義內(nèi)部類。我們可以創(chuàng)建一個類來輔助解決方案,而又不想這個類是共用的。如上,Inner定義在test方法中,所以在test方法外不能直接訪問Inner類。

 class Outer{
        public IInner getInner(){
            class  Inner implements IInner{

                @Override
                public void innerPrint() {
                    System.out.println("innerclass in innerPrint");
                }
            }
            return new Inner();
        }

    }
interface IInner{
        void innerPrint();
}

return 語句中用了向上轉(zhuǎn)型,當使用此方法時得到一個基類對象的引用就可以很好的隱藏實現(xiàn)細節(jié),通過這種方式可以完全阻止任何任何依賴于類型的編碼。也不是意味著getInner方法執(zhí)行結(jié)束了之后Inner就不可用了。你可以在同一個子目錄下的任意類使用Inner進行類標識,這并不會有命名沖突。

匿名內(nèi)部類

將上面例子修改下

 class Outer{
        public IInner getInner(){
            return new IInner() {
                @Override
                public void innerPrint() {
                    System.out.println("innerclass in innerPrint");
                }
            };
        }

匿名內(nèi)部類就是沒有名字的類,自動向上轉(zhuǎn)型為基類,在匿名內(nèi)部類使用了默認的構(gòu)造器來生成對象,那如果你的基類需要一個有參數(shù)的構(gòu)造器怎么辦呢?

public class Parcle {
    public Wrapping wrapping(int x){
        return new Wrapping(x){
            public  int value(){
                return super.value()*47;
            }
        };
    }
}

public class Wrapping {
    private int i;
    public Wrapping(int x){
        i=x;
    }
    public int value(){
        return i;
    }
}

只需要地傳遞合適的參數(shù)給基類的構(gòu)造器即可,這里是將x給new Wrapping(x),盡管Wrapping只是一個具有實現(xiàn)的普通類,但它還是被其導(dǎo)出類當作公共"接口"來使用。匿名內(nèi)部類與正規(guī)的繼承相比有些受限,因為匿名內(nèi)部類即可以擴展類,也可以實現(xiàn)接口,但是不能兩者兼?zhèn)?。而且如果實現(xiàn)接口也只能實現(xiàn)一個接口。
就像在android里面給View添加點擊事件監(jiān)聽都是實現(xiàn)一個匿名內(nèi)部類,如果在一個方法中定義,而想訪問方法里的局部變量,那么由于作用域的特性必需在局部變量前加上final,使其成為常量。

靜態(tài)內(nèi)部類

如果不需要內(nèi)部類與外圍類之間有聯(lián)系,那么可以將內(nèi)部類聲明為static。這樣內(nèi)部類的創(chuàng)建不需要依賴于外部類,因此靜態(tài)內(nèi)部類可能訪問外圍類的靜態(tài)成員。它沒有一個指向外圍類的引用。靜態(tài)內(nèi)部類與非靜態(tài)內(nèi)部類還有一個區(qū)別,普通內(nèi)部類的字段和方法,只能放在類的外部層次上,所以普通的內(nèi)部類不能有static和static 字段,也不能包含靜態(tài)內(nèi)部類,但是靜態(tài)內(nèi)部類可以。

接口內(nèi)部的類

正常情況下,不能在接口 內(nèi)部放置任何代碼,但靜態(tài)內(nèi)部類可以作為接口的一部份,放到接口中的任何類都是public和static的。如果你想要創(chuàng)建某此公共代碼,使得它們可以被某個接口的所有不同實現(xiàn)所共用,那么使用接口內(nèi)部的嵌套類會很方便。要做測試的話可以在每個類中都寫一個main()方法,用來測試這個類。這樣做有一個缺點,那就是必須帶著那些已編譯過的額外代碼。那我們就可以用嵌套類來放置測試代碼。

public class TestBed{
public void f(){
System.out.println("f()");
}
public static class Tester{
public static void main(String[] args){
TestBed t=new TestBed();
t.f();
}
}
}

這生成了一個獨立的類TestBed$Tester,不想在發(fā)布的產(chǎn)品中包含它,在將產(chǎn)品打包前可以簡單地刪除TestBed$Tester.class

說說內(nèi)部類的其它特性

多重嵌套內(nèi)部類訪問外部類的成員

一個內(nèi)部類被嵌套多少層并不重要,它能透明地訪問所有它所嵌入的外圍類的所有成員。

class MNA{
private void f(){}
class A{
private void g(){}
public class B{
void h(){
f();
g();
}
}
}
}

閉包

閉包是一個可調(diào)用的對象,它記錄了一些信息,這些信息來自于創(chuàng)建它的作用域。通過這個定義,可以看到非靜態(tài)內(nèi)部類是面向?qū)ο蟮拈]包,因為它不僅包含外圍類對象的信息,還自動擁有一個指向此外圍類對象的引用,在此作用域內(nèi),內(nèi)部類有權(quán)操作所有的成員,包括private成員。

內(nèi)部類的繼承

因為非靜態(tài)內(nèi)部類的構(gòu)造器必須連接到指向其外圍類的引用,所以在繼承內(nèi)部類的時候情況會有些復(fù)雜。問題在于指向外圍類對象的“秘密的”引用必須被初始化,而在導(dǎo)出類中不再存在可連接的默認對象。要解決這個問題,必須使用特殊的語法來明確說清楚它們之間的關(guān)聯(lián):

class WithInner{
class Inner{}}
public class InheritInner extends WithInner.Inner{
InheritInner(WithInner wi){
wi.super();
}
public static void main(String[] args){
WithInner wi=new WithInner();
InheritInner ii=new InheritInner(wi);
}
}

可以看到InheritInner只繼承自內(nèi)部類,而不是外圍類,但是當要生成一個構(gòu)造器時,默認的構(gòu)造器并不算好,而且不能只是傳遞一個指向外圍類的引用 。此外,必須在構(gòu)造器中使用如下語法:

wi.super();

內(nèi)部類可以被覆蓋嗎

public class Egg {
    private Yolk y;
    protected class Yolk{
        public Yolk(){
            System.out.println("Egg.Yolk()");
        }
    }
    public Egg(){
        System.out.println("New Egg()");
        y=new Yolk();
    }
}

public class BigEgg extends Egg {
    public class Yolk{
        public Yolk(){
            System.out.println("BigEgg.Yolk()");
        }
    }
    public static void main(String[] args){
        new BigEgg();
    }
}

運行結(jié)果

New Egg()
Egg.Yolk()

這個例子說明,當繼承了某個外圍類的時候,內(nèi)部類并沒有發(fā)生什么特別神奇的變化。這兩個內(nèi)部類是完全獨立的個體,各自在自己的命名空間里。當然,明確地繼承某個內(nèi)部類也是可以的。

內(nèi)部類標識符

由于每個類都會產(chǎn)生一個.class文件,其中包含了如何創(chuàng)建該類型的對象的全部信息,當然內(nèi)部類也會產(chǎn)生一個.class文件,這些類文件 的命名有嚴格的規(guī)則,外圍類的名字加上$再加上內(nèi)部類的名字,如果是匿名內(nèi)部類則是外圍類名加$加上數(shù)字。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java 內(nèi)部類 分四種:成員內(nèi)部類、局部內(nèi)部類、靜態(tài)內(nèi)部類和匿名內(nèi)部類。 1、成員內(nèi)部類: 即作為外部類的一個成...
    ikaroskun閱讀 1,355評論 0 13
  • 一:java概述:1,JDK:Java Development Kit,java的開發(fā)和運行環(huán)境,java的開發(fā)工...
    ZaneInTheSun閱讀 2,812評論 0 11
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,697評論 18 399
  • 今天整理一下內(nèi)部類,其中包含了內(nèi)部類的特殊形式,對比普通類有什么區(qū)別和作用,內(nèi)部類和外圍類之間的聯(lián)系,內(nèi)部類的擴展...
    _小二_閱讀 814評論 0 3
  • 今天在做任務(wù)的時候,發(fā)現(xiàn)在小米2上錄音的時候會彈出如下對話框: 感覺在小米2上的坑超多,可是我不想彈出這個對話框,...
    扈扈哈嘿閱讀 633評論 0 0

友情鏈接更多精彩內(nèi)容