歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java之內部類詳解

序言

有位小同學要我寫一篇這個的總結,我說那好吧,那就動手寫總結一下這個內部類的知識,感覺這個在面試中也會經常遇到,內部類、反射、集合、IO流、異常、多線程、泛型這些重要的基礎知識大家都比較容易記不住。大概是自己平常用的比較少,所以經常性的會忘記,現在通過博文的方式記錄下來,以後忘記可以回過頭來自己看。

一、什麼是內部類

顧名思義,內部類就是在一個類的內部在定義一個類,比如,A類中定義一個B類,那麼B類相對A類來說就稱為內部類,而A類相對B類來說就是外部類了。

在一個類中有很多屬性,比如、成員變量,成員方法,局部變量,靜態方法等(這些區別應該知道把,如果不知道的應該先把這幾個弄清楚,然後在繼續往下看,對,說的就是你),那麼這樣一比較,B類在A類中的不同位置就可以被分為了不同的內部類了。總共有四種,

1、成員內部類

2、局部內部類

3、靜態內部類

4、匿名內部類

現在看的一臉懵逼沒關系,接下來我會一一講解這四種內部類和用法及區別

二、成員內部類

1、什麼是成員內部類?

例如:  Outer類中有一個私有的成員變量id、還有一個out方法。 在內部類Inner中只有一個in方法  (注明:寫的類中沒有具體意義,就是為了說明問題而寫的)      

//在A類中申明了一個B類,此B類就在A的內部,並且在成員變量的位置上,所以就稱為成員內部類
public class Outer {
    private int id;
    public void out(){
        System.out.println("這是外部類方法");
    }
   
    class Inner{       
        public void in(){
            System.out.println("這是內部類方法");
        }
    }
}

2、如何實例化成員內部類呢?

public class Outer {
    private int id;
    public void out(){
        System.out.println("這是外部類方法");
    }
   
    class Inner{       
        public void in(){
            System.out.println("這是內部類方法");
        }
    }
}

----------------------------------------------------------------
public class Test{
        public static void main(String[] args) {
        //實例化成員內部類分兩步
        //1、實例化外部類
        Outer outObject = new Outer();
        //2、通過外部類調用內部類
        Outer.Inner inObject = outObject.new Inner();
     //測試,調用內部類中的方法
     inObject.in();//打印:這是內部類方法
    }
}

 分析:這個由外部類創建內部類對象不難理解把,想想如果你要使用一個類中方法或者屬性,你就必須要先有該類的一個對象,同理,一個類在另一個類的內部,那麼想要使用這個內部類,就必須先要有外部類的一個實例對象,然後在通過該對象去使用內部類。

3、成員內部類能做哪些事情?

·訪問外部類的所有屬性(這裡的屬性包括私有的成員變量,方法),例子大多數還是上面的內容,稍微改變一點點

public class Outer {
    private int id;
    public void out(){
        System.out.println("這是外部類方法");
    }
   
    class Inner{   
        public void in(){
            System.out.println("這是內部類方法");
        }
     
     //內部類訪問外部類私有的成員變量
     public void useId(){
       System.out.println(id+3);。
     }

  //內部類訪問外部類的方法 
     public void useOut(){
       out(); 
     } 
    }
}
--------------------------------------------------

public class Test{
        public static void main(String[] args) {
        //實例化成員內部類分兩步
        //1、實例化外部類
        Outer outObject = new Outer();
        //2、通過外部類調用內部類
        Outer.Inner inObject = outObject.new Inner();
     //測試
     inObject.useId();//打印3,因為id初始化值為0,0+3就為3,其中在內部類就使用了外部類的私有成員變量id。
     inObject.useOut();//打印:這是外部類方法
    }
}
 

如果內部類中的變量名和外部類的成員變量名一樣,那麼如何會怎麼樣呢?

public class Outer {
    private int id;//默認初始化0
    public void out(){
        System.out.println("這是外部類方法");
    }
   
    class Inner{   
        private int id=8;    //這個id跟外部類的屬性id名稱一樣。
        public void in(){           
            System.out.println("這是內部類方法");
        }
       
        public void test(){
            System.out.println(id);//輸出8,內部類中的變量會暫時將外部類的成員變量給隱藏
            //如何調用外部類的成員變量呢?通過Outer.this,想要知道為什麼能通過這個來調用,就得明白下面這個原理
            //想實例化內部類對象,就必須通過外部類對象,當外部類對象來new出內部類對象時,會
            //把自己(外部類對象)的引用傳到了內部類中,所以內部類就可以通過Outer.this來訪問外部類的屬性和方法,到這裡,你也就可以知道為什麼內部類可以訪問外部類的屬性和方法,這裡由於有兩個相同的
屬性名稱,所以需要顯示的用Outer.this來調用外部類的屬性,平常如果屬性名不重復,那麼我們在內部類中調用外部類的屬性和方法時,前面就隱式的調用了Outer.this。

            System.out.println(Outer.this.id);//輸出外部類的屬性id。也就是輸出0
        }
    }   
}

 借助成員內部類,來總結內部類(包括4種內部類)的通用用法:

1、要想訪問內部類中的內容,必須通過外部類對象來實例化內部類。

2、能夠訪問外部類所有的屬性和方法,原理就是在通過外部類對象實例化內部類對象時,外部類對象把自己的引用傳進了內部類,使內部類可以用通過Outer.this去調用外部類的屬性和方法,一般都是隱式調用了,但是當內部類中有屬性或者方法名和外部類中的屬性或方法名相同的時候,就需要通過顯式調用Outer.this了。

 

 4、成員內部類的面試題

//要求:使用已知的變量,在控制台輸出30,20,10。
     class Outer {
            public int num = 10;
            class Inner {
                public int num = 20;
                public void show() {
                    int num = 30;
                    System.out.println(?);
                    System.out.println(??);
                    System.out.println(???);
                }
            }
        }

        class InnerClassTest {
            public static void main(String[] args) {
                Outer.Inner oi = new Outer().new Inner();
                oi.show();
            }   
        }       

 答案:num、this.num、Outer.this.num

 解析:這題你如何明白了上面總結中的第二點,那麼非常簡單,考察的就是1、局部變量 2、this,和3、Outer.this,也就是內部類訪問外部類屬性方法的原理。這考察三個東西,

1、在一個方法中,使用直接使用變量名,肯定使用的是局部變量,因為會把大的成員變量給隱藏掉,這題中,也就是說show方法中的num會將內部類中的成員變量num隱藏掉,而內部類中的成員變量num又會把外部類中的成員變量num隱藏掉,所以通過直接輸出num,會是show方法中的局部變量的值30. 

2、使用this.num調用,其this代表的是調用該方法的對象,調用show方法的是oi,oi也就是內部類對象,所以oi.num也就標識內部類的成員變量num的值20

3、Outer.this.num,調用的外部類中的成員變量num的值也就是10,這個如果不清楚就看上面總結中的第二點,就是那個原理。

三、局部內部類

 1、什麼是局部內部類,跟局部變量一樣,在一個方法中定義的,那麼就是局部內部類了。

 例如

public class Outer {
    private int id;
  //在method01方法中有一個Inner內部類,這個內部類就稱為局部內部類
    public void method01(){class Inner{
            public void in(){
                System.out.println("這是局部內部類");
            }
        }
    }
}

  2、局部內部類一般的作用跟在成員內部類中總結的差不多,但是有兩個要注意的地方,

 1、在局部內部類中,如果要訪問局部變量,那麼該局部變量要用final修飾。看例子 

為什麼需要使用final?

 final修飾變量:變為常量,會在常量池中放著,

 逆向思維想這個問題,如果不實用final修飾,當局部內部類被實例化後,方法彈棧,局部變量隨著跟著消失,這個時候局部內部類對象在想去調用該局部變量,就會報錯,因為該局部變量已經沒了,當局部變量用fanal修飾後,就會將其加入常量池中,即使方法彈棧了,該局部變量還在常量池中呆著,局部內部類也就是夠調用。所以局部內部類想要調用局部變量時,需要使用final修飾,不使用,編譯度通不過。

public class Outer {
    private int id;
    public void method01(){
        final int cid = 3;    //這個就是局部變量cid。要讓局部內部類使用,就得變為final並且賦值,如果不使用final修飾,就會報錯
        class Inner{
            //內部類的第一個方法
            public void in(){   
                System.out.println("這是局部內部類");
            }
            //內部類中的使用局部變量cid的方法
            public void useCid(){   
                System.out.println(cid);
            }
        }
       
    }
}

 2、局部內部類不能通過外部類對象直接實例化,而是在方法中實例化出自己來,然後通過內部類對象調用自己類中的方法。看下面例子就知道如何用了

public class Outer {
    private int id;
   
    public void out(){
        System.out.println("外部類方法");
    }
    public void method01(){
        class Inner{
            public void in(){   
                System.out.println("這是局部內部類");
            }
        }
//關鍵在這裡,如需要在method01方法中自己創建內部類實例,然後調用內部類中的方法,等待外部類調用method01方法,就可以執行到內部類中的方法了。
        Inner In = new Inner();
        In.in();
    }
}

總結:局部內部類就只要注意那亮點就行了

1、在局部內部類中,如果要訪問局部變量,那麼該局部變量要用final修飾

2、如何調用局部內部類方法。

四、靜態內部類

根據名字就知道,使用static修飾的內部類,就叫做靜態內部類了。     

回顧static的用法:一般只修飾變量和方法,平常不可以修飾類,但是內部類卻可以被static修飾。

1、static修飾成員變量:整個類的實例共享靜態變量

2、static修飾方法:靜態方法,只能夠訪問用static修飾的屬性或方法,而非靜態方法可以訪問static修飾的方法或屬性

3、被static修飾了的成員變量和方法能直接被類名調用。

4、static不能修飾局部變量,切記,不要搞混淆了,static平常就用來修飾成員變量和方法。

注意:

1、我們上面說的內部類能夠調用外部類的方法和屬性,在靜態內部類中就行了,因為靜態內部類沒有了指向外部類對象的引用。除非外部類中的方法或者屬性也是靜態的。這就回歸到了static關鍵字的用法。

2、靜態內部類能夠直接被外部類給實例化,不需要使用外部類對象

Outer.Inner inner = new Outer.Inner();

3、靜態內部類中可以什麼靜態方法和靜態變量,但是非靜態內部類中就不可以申明靜態方法和靜態變量

五、匿名內部類      

這個在四種內部類中,使用的是最多的,在以後遇到的程序中,大多數出現的也就是它。

什麼是匿名對象?如果一個對象只要使用一次,那麼我們就是需要new Object().method()。 就可以了,而不需要給這個實例保存到該類型變量中去。這就是匿名對象。例如 

public class Test {
    public static void main(String[] args) {
        //講new出來的Apple實例賦給apple變量保存起來,但是我們只需要用一次,就可以這樣寫
        Apple apple = new Apple();
        apple.eat();
        //這種就叫做匿名對象的使用,不把實例保存到變量中。
        new Apple().eat();
    }
}
class Apple{
    public void eat(){
        System.out.println("我要被吃了");
    }
}

 

匿名內部類跟匿名對象是一個道理,

匿名對象:我只需要用一次,那麼我就不用聲明一個該類型變量來保存對象了,

匿名內部類:我也只需要用一次,那我就不需要在類中先定義一個內部類,而是等待需要用的時候,我就在臨時實現這個內部類,因為用次數少,可能就這一次,那麼這樣寫內部類,更方便。不然先寫出一個內部類的全部實現來,然後就調用它一次,豈不是用完之後就一直將其放在那,那就沒必要那樣。

匿名內部類是如何個格式呢?

前提:存在一個類或者接口 
                為什麼呢?
               例如:A接口  new A(){};  必須實現A接口中所有抽象方法,實現完之後,new A(){} 這個整體就是A的一個匿名實現類對象,肯定可以調用自己類中的方法
                    不一定是接口,可以是抽象類,也可以是完整的類,如果是抽象類的話,跟接口一樣,實現抽象的方法,如果是完整的類,就可以重寫自己所需要的方法,然後在調用,

舉個例子,看看不使用匿名內部類和使用了匿名內部類的區別

不實用匿名內部類,真麻煩啊。

public class Test {
    public static void main(String[] args) {
    //如果我們需要使用接口中的方法,我們就需要走3步,1、實現接口 2、創建實現接口類的實例對象 3、通過對象調用方法
      //第二步     
     Test02 test = new Test02();
      //第三步
        test.method();
    }
}

//接口Test1
interface Test01{
    public void method();
}
//第一步、實現Test01接口
class Test02 implements Test01{

    @Override
    public void method() {
        System.out.println("實現了Test接口的方法");
    }
   
}

 使用了匿名內部類。

public class Test {
    public static void main(String[] args) {
      //如果我們需要使用接口中的方法,我們只需要走一步,就是使用匿名內部類,直接將其類的對象創建出來。
        new Test1(){
        public void method(){
          System.out.println("實現了Test接口的方法");
        }
      }.method();

    }
}

interface Test1{
    public void method();
}

解析:其實只要明白一點,new Test1(){實現接口中方法的代碼};  Test1(){...}這個的作用就是將接口給實現了,只不過這裡實現該接口的是一個匿名類,也就是說這個類沒名字,只能使用這一次,我們知道了這是一個類, 將其new出來,就能獲得一個實現了Test1接口的類的實例對象,通過該實例對象,就能調用該類中的方法了,因為其匿名類是在一個類中實現的,所以叫其匿名內部類,不要糾結為什麼Test1(){...}就相當於實現了Test1接口,這其中的原理等足夠強大了,在去學習,不要鑽牛角尖,這裡就僅僅是需要知道他的作用是什麼,做了些什麼東西就行。

匿名內部類的面試題: 

* A:面試題
*
        按照要求,補齊代碼
        interface Inter { void show(); }
        class Outer { //補齊代碼 }
        class OuterDemo {
            public static void main(String[] args) {
                  Outer.method().show();
              }
        }
        要求在控制台輸出”HelloWorld”

 答案:

* A:面試題
*
        按照要求,補齊代碼
        interface Inter { void show(); }
        class Outer {
      //補齊代碼
      public static Inter method(){
        return new Inter(){
          void show(){
            System.out.println("HelloWorld");
          }
        };
      }
     }
        class OuterDemo {
            public static void main(String[] args) {
                  Outer.method().show();
              }
        }
        要求在控制台輸出”HelloWorld”

 

解析:這個題目挺有趣的,考了很多東西,一開始只能說我自己度懵逼了,是一個局部匿名內部類。  通過看主方法中的調用,Outer.method(),能直接用類名調用方法,那麼肯定該方法就是一個static方法,然後又直接調用了show()方法,說明這個method方法有一個返回值,其返回值類型就是實現該接口類的類型,因為只有接口中有show()這個方法。所以在method中就是一個匿名內部類。

Copyright © Linux教程網 All Rights Reserved