java 靜態內部類小總結

內部類是指在一個外部類的內部再定義一個類。內部類做爲外部類的一個成員,而且依附於外部類而存在的。內部類可爲靜態,可用protected和private修飾(而外部類只能使用public和缺省的包訪問權限)。內部類主要有如下幾類:成員內部類、局部內部類、靜態內部類、匿名內部類

爲何須要內部類?
典型的狀況是,內部類繼承自某個類或實現某個接口,內部類的代碼操做建立其的外圍類的對象。因此你能夠認爲內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的緣由是:
每 個內部類都能獨立地繼承自一個(接口的)實現,因此不管外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。若是沒有內部類提供的能夠繼承多 個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實 現了「多重繼承」。java

靜態內部類型(static nested type)

靜態內部類型能夠是class,interface,或者enum。而其餘類型的內部類型只能是class。shell

靜態內部類其實仍是一個頂層類(源代碼文件級別的類),他不依賴外部類,只不過它被封裝在另外一個class或者interface中,而不是直接定義在文件級別中。所以,它和通常的類靜態成員很相似:編程

一、它不包含外部類當前對象引用this,所以不能直接訪問外部類的實際成員,但可使用外部類的static成員。數據結構

二、靜態內部類做爲一個靜態成員,所以能夠用訪問權限修飾符:public . private .......等。用的最多通常是private閉包

    引用靜態內部類:  Wapper.Innerapp

三、不能在非靜態內部類中再定義靜態內部類。靜態內部類能夠無限深度的嵌套下去。jvm

 

提高ide

內部類最終會被javac編譯爲獨立的類,JVM看見的都是top-level類。函數

編譯後的class文件形如:WrapperClass $ InnerStaticClass.classthis

 

下面是使用靜態內部類簡單實現鏈式棧數據結構的例子。

class LinkedSatck<E> {

    private static class Node<T> {

        public Node(Node<T> next, T data) {
            this.next = next;
            this.data = data;
        }

        @SuppressWarnings("unused")
        public Node() {
            this(null, null);
        }

        private Node<T> next;
        private T data;

        public T getData() {
            return data;
        }

        public Node<T> getNext() {
            return next;
        }

        public boolean isEndNode() {
            return (next == null);
        }

    }   



    public LinkedSatck() {
        topNode = new Node<E>(null, null);

        // size =0;
    }

    private int size = 0;
    private Node<E> topNode = null;

    public void push(E e) {

        Node<E> newTopNode = new Node<E>(topNode, e);
        ++size;
        topNode = newTopNode;

    }

    public E pop() {
        if (topNode.isEndNode())
            return null;
        else {
            E re = topNode.getData();

            topNode = topNode.getNext();

            --size;
            return re;

        }
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
        // return topNode.isEnd();
    }

}
View Code

成員內部類(inner member class)

成員內部類最重要的特色就是它能夠直接使用外部類的實例成員和static成員,即使是使用private修飾也是如此。 

由於成員內部類總包含了一個外部類的當前對象引用 ,奇怪的名字 this$0,這個引用在成員內部類實例化時被外部類的當前對象引用this初始化。

大體實現以下:

class Outter
{
    private class Inner
    {
        private final Outter this$0;  //javac自動添加
        Inner(Outter o)         //javac自動添加
        {
            this.this$0 = o;
        }
    }
    //使用成員內部類對象時發生:new Inner(Outter.this)
}
View Code

 這也是爲何在建立一個成員內部類對象時,要先建立一個外部類對象的緣由了。

 

和普通實例成員同樣,成員內部裏是屬於外部類的對象的,那麼,在成員內部類就理所固然能夠直接使用外部類的其餘實例成員以及static成員。

由於是實例成員,因此可使用訪問修飾符:public 、protected、private、和默認的包訪問權限。

由於是實例成員,所以,在類的外部使用內部類時,必須先建立1個外部類對象,在實際開發中不多使用這個。

class Outter
{
    public class Inner
    {
        
    }
}

public class Test 
{
public static void main(String[] args) {

        Outter out = new  Outter();
        Outter.Inner in = out.new Inner();
    }

}
View Code

 一個例子,自定義一個Str類,來支持迭代。

class Str implements Iterable<Character>
{
    
    private String innerStr;

    public Str(String s)
    {
        this.innerStr = (s==null?"":s);
    }
    
    
    private class StrIterator implements Iterator<Character>    //迭代器類 做爲成員內部類
    {
        
        private int curIndex = 0;

        @Override
        public boolean hasNext() {
            
            return curIndex < innerStr.length();    //直接訪問外部類的成員 innerStr
        }

        @Override
        public Character next() {
            
            return innerStr.charAt(curIndex++);   //直接訪問外部類的成員 innerStr
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
            
        }
        
    }
    
    @Override
    public Iterator<Character> iterator() {
        
        return new StrIterator();
    }
    
}
View Code 

成員內部類中不能有static成員,即不能有static方法 和 static字段(除非static字段修飾爲static final,那樣的話它只不過是一個符號常量罷了)。由於java中類的靜態成員必須定義在一個top-level頂層類中,而成員內部類(包括後面的方法內部類,匿名內部類)不是top-level頂層類。

static成員須要定義在top-level類中,而成員內部類不是top-level類。

 

 

提高

JVM是不理解nested類型的,也就是在它看來,全部的類型都是top-level的, 

在每個成員內部類中,javac都會自動添加一個字段:this$0,用來引用外部類當前對象。同時, 內部類的構造函數會自動爲這個字段添加一個參數,當構造內部類對象時,

外部類當前對象就會傳遞給this$0,讓這個字段引用外部類當前實例對象。

從這點咱們也會發現,爲何要實例化一個成員內部類前,須要先實例化一個外部類對象。由於成員內部包含了一個外部類對象。

 

編譯後的class文件形如:WrapperClass $ InnerClass.class

 

 

局部內部類(local inner class)

定義在一個方法(包括了類的構造塊和static構造塊)內部的類,叫局部內部類。它不能有任何訪問權限修飾符,由於它只能被包裝它的方法使用,離開方法後就不可用了。

局部內部類能夠和成員內部類同樣,訪問外部類的實例成員。同時,它還能直接使用包含它的方法的局部final常量,final參數。javac會複製使用了的外部方法的局部final量保存在局部內部類中做爲私有的備份

所以,當這個外部方法執行完畢後,雖然方法中的局部變量的 lifetime結束了,可是若是局部類的實例做爲返回值,它會帶着外部方法的局部final量離開這個局部做用域,也就是說,局部變量的生命延長到了和局部內部類的對象的生命週期一致。並不會隨着方法執行完馬上被清理掉。咱們能夠以此來造成閉包

一樣,局部內部類不是top-level類,不能有static成員,除非是static final 字段。

 

public class Main
{
    public static void main(String[] args) 
    {
        
        MsgGenerator g5 = fac(5);
        System.out.println(g5.generatorMsg());
        
        MsgGenerator g2 = fac(2);
        System.out.println(g2.generatorMsg());
        
    }
   
    public static MsgGenerator fac(final int times)
    {
        class Generator implements MsgGenerator
        {
            @Override
            public StringBuffer generatorMsg()
            {
                
                StringBuffer s=  new StringBuffer() ;
                
                for(int i=0;i<times;++i)
                {
                    s.append("hello  ");
                }
                
                return s;
            }
            
            
        }  //end of class
        
        
        return new Generator();    //向外發出閉包
    }
   
}


interface MsgGenerator
{
    StringBuffer generatorMsg();
}
View Code

提高:

局部內部類之因此能訪問外部類的實例成員,其緣由和成員內部類是同樣的:內部類中有保存了外部類對象的引用。除此以外,局部內部類還能訪問包裝方法的final字段,javac會將內部類使用了的final 局部常量拷貝到局部內部類中保存,並在局部內部類對象實例化時,初始化這些final常量。所以,局部內部類使用的final常量是本身的拷貝分。

 

局部內部類的實現原理(模擬)

class Wapper
{
    
    public void wapperFunction()       //在方法中定義一個局部類
    {
        final int x = 10;
        
        class Local       //局部類
        {
            
            private final int local_copy_x;   //假如在局部類中使用了局部常量x,則javac自動生成
            
            private final Wapper this$0;            //javac自動生成的字段,用於保存外部類當前對象引用
            
            
            //首先,局部內部類一定會包含外部類對象,着就是javac插入的第一個構造參數,這是一定的。
            //其次,若是咱們在局部內部類中使用了包裝方法foo中的局部final常量,如x,則會在局部類中
            //自動添加隱藏字段local_copy_x,並在構造器中初始化它。
            Local(Wapper w,final int x)
            {
                this$0 = w;
                local_copy_x = x;
            }
            

        }//end of Local
        
       
      
     new Local();     //當實例化局部內部類對象時,等價於 new Local(Wapper.this,x)
    
    }
    
}
View Code 

因此,之因此能在局部內部類中訪問外部類的實例,是由於javac自動添加並用外部類當前對象this初始化了局部內部類的字段this$0,這樣this$0就引用了外部類當前對象。

局部內部類能使用包裝 方法的final字段,也是由於javac自動在局部內部類中添加並初始化的結果。

 

 

 

 

 

匿名內部類 (inner anonymous class)

匿名內部類是特殊的局部內部類,它沒有類名。它的訪問特性和局部內類同樣。若是隻會使用類的一個對象,則可使用匿名內部類,沒有名稱避免了再引入一個類名稱, 匿名內部類是沒有名稱的局部內部類,訪問特性與局部內部類同樣。

 

由於沒有類名,所以只能使用父類名或者接口名來建立對象。

new + superClass  或者 new+interface  。建立對象 是 使用new表達式。

new 表達式:匿名類對象的建立方式是使用new表達式,建立對象的同時也是類結構的編寫。表達式的值是一個匿名類對象的引用。

new  SuperClass(param1,param2){   類體    }

通常來講,匿名類沒有構造參數,若是有,則傳遞給他的父類的構造函數。

 

匿名內部類因爲沒有類名,因此你不能定義新的構造函數,只能有默認的構造函數(javac添加的)。補救的作法是使用構造塊。

 

下面是使用swing 中的Timer定時觸發回調函數的例子,使用匿名類建立 ActionListener對象。程序每通過1000ms,就會調用 ActionListener對象的actionPerformed方法。

public static void main(String[] args) {


        //javax.swing.Timer;
        Timer t = new Timer(1000, 
                            new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) 
                        {
                            Toolkit.getDefaultToolkit().beep();  //系統響鈴聲    
                        }
                    }
                    
            );
        
        t.start();
        
        while(true)
        {
            
        }
        

    }
View Code

內部類的工做原理

內部類只是Java的語法糖,jvm是不理解內部類的,它所看見的都是top-level頂層類。將內部類分離爲單獨的頂層類,是javac 的任務。內部類被javac合成爲單獨的類,並造成獨立的class文件,這個文件有獨特的名稱,形式以下:

static內部類:  OutterClass$InnerClass.class

成員內部類:OutterClass$InnerClass.class

局部內部類:  OutterClass$XInnerClass.class           # X爲一個正整數

局部內部類:  OutterClass$X.class                          # X爲一個正整數

 

對於static內部類,無需多解釋,由於 static內部類和外部類是無依賴關係的,static內部類不包含外部類引用,javac只是將他們簡單的分離。

 

成員內部類爲何能訪問外部類的成員?由於內部類會被javac自動插入一個字段this&0去保存外部類當前對象this的引用。

public class OutterClass
{
    
    private int outFiled = 100;
    
    public class InnerClass
    {
        
        public void f()
        {
            int i = outFiled;
        }
    }
}
View Code

 javap反編譯後的結果

Compiled from "OutterClass.java"
public class OutterClass$InnerClass {
  final OutterClass this$0;      //由javac自動合成:內部類包含1個外部類的當前對象的引用this&0,使用this&0避免和this衝突    
    
  //javac自動合成:合成的構造函數,有1個外部類參數,用於初始化 this&0,this&0被賦值爲OutterClass.this
  public OutterClass$InnerClass(OutterClass);
    Code:
       0: aload_0       
       1: aload_1       
       2: putfield      #1                  // Field this$0:LOutterClass;
       5: aload_0       
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return        




  public void f();
    Code:
       0: aload_0       
       1: getfield      #1                  // Field this$0:LOutterClass;
       4: invokestatic  #3                  // Method OutterClass.access$000:(LOutterClass;)I
       7: istore_1      
       8: return        
}
View Code

局部內部類和匿名內部類除了能夠訪問外部類的成員(緣由和成員內部類是相同的),還能夠訪問外部方法的局部final量和final參數。緣由以下:

A local class can use local variables because javac automatically gives the class a
private instance field to hold a copy of each local variable the class uses.
The compiler also adds hidden parameters to each local class constructor to initial‐
ize these automatically created private fields. A local class does not actually access
local variables but merely its own private copies of them. This could cause inconsis‐
tencies if the local variables could alter outside of the local class.

                                                                         -- 《Java int a Nutshell》

由於javac會自動在(局部和匿名)內部類中插入私有 的實例字段來保存 使用了的外部方法的final量的拷貝。javac還會在(局部和匿名)內部類中的構造函數的添加參數來初始化插入的私有 的字段。因此,(局部和匿名)內部類使用的實質是本身得到的拷貝量,而不是直接使用外部方法的final量。若是外部方法的量不修飾爲final的話,那麼意味着它的值能夠改變,這就可能會致使(局部和匿名)內部類中得到的拷貝和外部方法不一致。因此java強制要求只能使用final量。

靜態內部類和非靜態內部類的區別

若是你不須要內部類對象與其外圍類對象之間有聯繫,那你能夠將內部類聲明爲static。這一般稱爲嵌套類(nested class)。Static Nested Class是被聲明爲靜態(static)的內部類,它能夠不依賴於外部類實例被實例化。而一般的內部類須要在外部類實例化後才能實例化。想要理解static應用於內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向建立它的外圍類對象。然而,當內部類是static的時,就不是這樣了。嵌套類意味着: 

1. 嵌套類的對象,並不須要其外圍類的對象。 

2. 不能從嵌套類的對象中訪問非靜態的外圍類對象。 

以下所示代碼爲定義一個靜態嵌套類

public class StaticTest{
   private static String name = "woobo";
   private String num = "X001";
   static class Person{ // 靜態內部類能夠用public,protected,private修飾 

       // 靜態內部類中能夠定義靜態或者非靜態的成員  
     private String address = "China";

Private Static String x=「as」;
     public String mail = "kongbowoo@yahoo.com.cn";//內部類公有成員
     public void display(){
       //System.out.println(num);//不能直接訪問外部類的非靜態成員

// 靜態內部類不能訪問外部類的非靜態成員(包括非靜態變量和非靜態方法)
       System.out.println(name);//只能直接訪問外部類的靜態成員

//靜態內部類只能訪問外部類的靜態成員(包括靜態變量和靜態方法)
       System.out.println("Inner " + address);//訪問本內部類成員。
     }
   }
   public void printInfo(){
     Person person = new Person();

// 外部類訪問內部類的非靜態成員:實例化內部類便可 

person.display();

     //System.out.println(mail);//不可訪問
     //System.out.println(address);//不可訪問
     System.out.println(person.address);//能夠訪問內部類的私有成員

System.out.println(Person.x);// 外部類訪問內部類的靜態成員:內部類.靜態成員
     System.out.println(person.mail);//能夠訪問內部類的公有成員
   }
   public static void main(String[] args){
     StaticTest staticTest = new StaticTest();
     staticTest.printInfo();
   }
}
 


在靜態嵌套類內部, 不能訪問外部類的非靜態成員, 這是由Java語法中"靜態方法不能直接訪問非靜態成員"所限定.注意, 外部類訪問內部類的的成員有些特別, 不能直接訪問, 但能夠經過內部類實例來訪問, 這是由於靜態嵌套內的全部成員和方法默認爲靜態的了.同時注意, 內部靜態類Person只在類StaticTest 範圍內可見, 若在其它類中引用或初始化, 均是錯誤的.
一 . 靜態內部類能夠有靜態成員,而非靜態內部類則不能有靜態成員。 
二 . 靜態內部類的非靜態成員能夠訪問外部類的靜態變量,而不可訪問外部類的非靜態變量;

三 . 非靜態內部類的非靜態成員能夠訪問外部類的非靜態變量。

    生成一個靜態內部類不須要外部類成員:這是靜態內部類和成員內部類的區別。靜態內部類的對象能夠直接生成:Outer.Inner in = new Outer.Inner();而不須要經過生成外部類對象來生成。這樣實際上使靜態內部類成爲了一個頂級類(正常狀況下,你不能在接口內部放置任何代碼,但嵌套類能夠做爲接口的一部分,由於它是static 的。只是將嵌套類置於接口的命名空間內,這並不違反接口的規則)

相關文章
相關標籤/搜索