天天看點

面向對象(17):内部類及其案例

面向對象(17):内部類及其案例

一、内部類的介紹

1、内部類的概述

把類定義在其他類的内部,我們稱之為内部類
舉例:在類A中定義了一個類B,類B就是内部類
           

2、内部類的特點

(1)内部類可以通路外部類的成員,包括私有和靜态的外部類成員
(内部類如果是靜态的,就不能通路外部類私有的,但是可以通路靜态的成員)

案例:
class Outer{
    private int num = 10;

    class Inner{
        public void show(){
            System.out.println(num);
        }
    }
}

           
(2)外部類要想通路内部類的成員,就必須要建立内部類的對象

案例:
class Outer{
    private int num = 10;

    class Inner{
        public void show(){
            System.out.println(num);
        }
    }
//在外部類中建立一個方法,在方法中建立内部類對象,通過内部類對象來通路内部類成員
    public void show2(){
        //建立内部類對象
        Inner inner = new Inner();
        inner.show();
    }
}
           

3、内部類位置

根據内部類定義的位置不同,可以分為兩種類型:
(1)成員的位置上(成員内部類)
(2)局部的位置上(局部内部類),定義在方法内的

案例:
class Outer2{

    //成員内部類
    class Inner2{
    }
    
    public void fun(){
        //局部内部類
        class Inner3{
        }
    }
}
           

4、成員内部類的修飾符

成員内部類常見的修飾符為:private、static
            private:為了保證資料安全性
            static:為了友善通路資料
           
靜态修飾的特點:
    内部類如果是靜态修飾的,隻能通路外部類靜态的成員
           

案例:

class Outer4{
    private int num = 10;
    private static int num2 = 30;

    static class Inner4{
        public static void show2(){
	//System.out.println(num);靜态的内部類不能通路外部私有的,會報錯
            System.out.println(num2)//靜态的内部類可以通路外部類靜态的成員
        }
    }
           

二、成員内部類

1、如果内部類是非靜态的,在測試類中該如何通路内部類中的成員?

格式(1)外部類名.内部類名 對象名 = new 外部類名().new 内部類名();
			     = 外部類對象.内部類對象;
	(2)通過對象名調取
           

2、如果内部類是靜态的,在測試類中如何通路内部類中的成員呢?

如果内部類是靜态的,内部類成員是非靜态的 ,測試類中需要建立對象來調取内部類成員

外部類名.内部類名 對象名 = new 外部類類名.内部類類名();
			對象名.成員名;//調取靜态内部類的非靜态成員變量
			對象名.成員名();//調取靜态内部類的非靜态成員方法
           

如果内部類是靜态的,内部類成員也是靜态的 ,測試類中可以通過類名直接調用内部類成員

外部類名.内部類名.成員名;//調取靜态内部類的靜态成員變量
	外部類名.内部類名.成員名();//調取靜态内部類的靜态成員方法
           

3、案例:

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(???);
      }
   }
}

		需求:在控制分别輸出:30,20,10
           

答:

//外部類
class Outer5 {
    public int num = 10;
    //内部類
  class Inner5 {
      //成員内部類
      public int num = 20;
      public void show() {
         int num = 30;
         System.out.println(num);//這裡的num代表方法裡的局部變量30
            
         System.out.println(this.num);//this表示這裡的num代表目前類的成員變量 
         
            //Inner5與Outer5不是繼承關系,不能使用super關鍵字
			//System.out.println(super.num);
			
            System.out.println(Outer5.this.num);//表示目前外部類下的成員變量10
   //或者 System.out.println(new Outer5().num);通過外部類對象調取外部類成員變量 
        }
    }
}

public class InnerClassDemo5 {
    public static void main(String[] args) {
    	//内部類是非靜态的,成員也非靜态的
        Outer5.Inner5 oi5 = new Outer5().new Inner5();
        oi5.show();
    }
}
            執行結果如下:
                    30
                    20
                    10

                    Process finished with exit code 0
           

三、局部内部類

1、位置:定義在方法内的類稱之為局部内部類

2、局部内部類的特點

特點(1)可以直接通路外部類的所有成員

案例:
class Outer6{
    private int num = 10;

    public void fun(){
        int num2 = 100;
        //局部内部類
        class Inner6{
            int num3 = 200;
            //局部内部類中的方法
            public void show(){
        		System.out.println(num);//外部類的成員變量
       			System.out.println(num2);//外部類方法中的成員變量
       		 	System.out.println(num3);//局部内部類中的成員變量
            }
        }
           

如果想要實作上面局部内部類的show方法,該怎麼辦?

①需要在外部類成員方法内、局部内部類外,建立局部類對象

②在測試類中中建立外部類對象,調取外部類成員方法

class Outer6{
    private int num = 10;
	//外部類的成員方法
    public void fun(){
        int num2 = 100;
        //局部内部類
        class Inner6{
            int num3 = 200;
            //局部内部類中的方法
            public void show(){
        		System.out.println(num);//外部類的成員變量
       			System.out.println(num2);//外部類方法中的成員變量
       		 	System.out.println(num3);//局部内部類中的成員變量
           }
        }
        //需要在外部類的成員方法中建立内部類對象
        Inner6  inner6 = new Inner6();
        //調取内部類的show方法
        inner6.show();
      }
    }
    
//測試類
public class InnerClassDemo6 {
    public static void main(String[] args) {
    	//建立外部類對象
        Outer6 outer6 = new Outer6();
        //調取外部類成員方法
        outer6.fun();
    }
}
            執行結果如下:
             		10
                        100
                        200

                        Process finished with exit code 0
           

由上得出局部内部類的第二個特點:

特點(2)可以在外部類中的局部範圍中建立對象,通過對象調用内部類中的方法,來使用内部類的局部功能

3、局部内部類通路局部變量的注意事項

通過反編譯工具檢視後發現, 局部内部類存在的方法中定義的局部變量自動加上了final,不能進行二次指派
特點:jdk1.8之後會自動添加final關鍵字
           

四、匿名内部類

1、匿名内部類的含義

匿名内部類是内部類的一個簡化寫法
           

2、存在匿名内部類的前提

要存在一個類或者是一個接口,類可以是具體的類也可以是抽象類
           

3、定義格式

new 類名或者接口名(){重寫方法;}
           
本質上:
    是一個繼承了這個類或者實作了這個接口的子類匿名對象
           
/建立一個接口
interface  Inner{
    //定義兩個抽象方法
    public abstract void show1();
    public abstract void show2();
}

class Outer{
    //建立一個方法
    public void fun(){
    //匿名内部類
        new Inner(){
            //重寫方法
            @Override
            public void show1() {
                System.out.println("這是show1方法");
            }

            @Override
            public void show2() {
                System.out.println("這是show2方法");
            }
        }.show1();//new Inner(){}整體相對于new了一個接口的對象
                    //可以直接調用接口中的方法
    }
}

public class InnerClassDEmo11 {
    public static void main(String[] args) {
        //想要調用fun方法,必須先建立外部類對象
        Outer outer = new Outer();
        outer.fun();
    }
}
        執行結果如下:
                    這是show1方法

                    Process finished with exit code 0
//如果想要再調取show2方法,必須再建立一個匿名内部類,因為匿名對象隻能使用一次
           

想一想,如果接口中的方法很多的時候,每次調用一個方法,都需要new一下,要寫的内容都重複

了,怎麼改進?

使用多态的形式,這裡叫做接口多态,将匿名内部類用接口來接收一下

/建立一個接口
interface  Inner{
    //定義兩個抽象方法
    public abstract void show1();
    public abstract void show2();
}

class Outer{
    //建立一個方法
    public void fun(){
    //接口多态:用接口來接收一下匿名内部類
        Inter i = new Inner(){
            //重寫方法
            @Override
            public void show1() {
                System.out.println("這是show1方法");
            }

            @Override
            public void show2() {
                System.out.println("這是show2方法");
            }
        };
        i.show1();//在fun方法中直接調取show1()
        i.show2();//在fun方法中直接調取show2()
    }
}

public class InnerClassDEmo11 {
    public static void main(String[] args) {
        //想要調用fun方法,必須先建立外部類對象
        Outer outer = new Outer();
        outer.fun();
    }
}
        執行結果如下:
                    這是show1方法
                    這是show2方法

                    Process finished with exit code 0
           

學習了匿名内部類,接口的寫法又多了一個

之前:
	//接口
    interface A{
                b();
                c();
              }
              
	//建立一個類來實作接口
    class B implements A{
            b(){..}//重寫方法
            c(){..}//重寫方法
            }
            
	//在測試類中使用接口多态建立對象
        A a = new B();
    
現在:
    //接口
    interface A{
                b();
                c();
              }

     //在外部類中的一個方法内
     A a = new A(){
             	b(){..}//重寫方法
                c(){..}//重寫方法
            };

     //測試類中
   建立外部類對象,然後調取外部類的方法,就相當于實作了接口
           

五、匿名内部類在開發中的使用案例

實作接口的方法一:接口傳參

//在開發中,會有人提供一個接口給我們
interface Person{
    public abstract void study();
}

//定義一個類
class PersonDemo{
    //建立一個方法,接口作為形參傳入進去
    //當接口作為方法的參數的時候,實際上需要的是實作該接口類的對象
    public void fun(Person p){
        p.study();
    }
}

//定義一個類,來實作該接口
//當接口作為方法的參數的時候,需要的就是本類的對象
class Student66 implements Person{

    @Override
    public void study() {
        System.out.println("好好學習");
    }
}

//測試類
public class InnerClassDemo1 {
    public static void main(String[] args) {
        //想要調方法,先建立對象
        PersonDemo pd = new PersonDemo();
        Student66 s = new Student66();
        pd.fun(s);
    }
}
           

實作接口的方法二(進階):接口傳參、使用匿名對象

//在開發中,會有人提供一個接口給我們
interface Person{
    public abstract void study();
}

//定義一個類
class PersonDemo{
    //建立一個方法,接口作為形參傳入進去
    //當接口作為方法的參數的時候,實際上需要的是實作該接口類的對象
    public void fun(Person p){
        p.study();
    }
}

//定義一個類,來實作該接口
//當接口作為方法的參數的時候,需要的就是本類的對象
class Student66 implements Person{

    @Override
    public void study() {
        System.out.println("好好學習");
    }
}

//測試類
public class InnerClassDemo1 {
    public static void main(String[] args) {
        //想要調方法,先建立對象
        PersonDemo pd = new PersonDemo();
//        Student66 s = new Student66();
        pd.fun(new Student66());
    }
}
           

實作接口的方法三(更進階1):使用匿名内部類

不需要專門寫一個類來實作接口

//在開發中,會有人提供一個接口給我們
interface Person{
    public abstract void study();
}

//定義一個類
class PersonDemo{
    //建立一個方法,接口作為形參傳入進去
    //當接口作為方法的參數的時候,實際上需要的是實作該接口類的對象
    public void fun(Person p){
        p.study();
    }
}

//測試類
public class InnerClassDemo1 {
    public static void main(String[] args) {
        //想要調方法,先建立對象
        PersonDemo pd = new PersonDemo();
        pd.fun(new Person() {	//直接調取fun方法,将匿名内部類當參數傳入進去
            @Override
            public void study() {
                System.out.println("好好學習");
            }
        });
    }
}
           

實作接口的方法三(更進階2):使用構造方法

//在開發中,會有人提供一個接口給我們
interface Person{
    public abstract void study();
}

//定義一個類
class PersonDemo{
    Person p;
    //構造方法
    PersonDemo(Person p){
    this.p = p;
    }
}


//測試類
public class InnerClassDemo1 {
    public static void main(String[] args) {
     //使用匿名内部類的形式建立對象,并将匿名内部類當參數傳入進去
        PersonDemo pd = new PersonDemo(new Person() {
            @Override
            public void study() {
                System.out.println("好好學習");
            }
        });

        Person p = pd.p; //pd.p.var回車
        p.study();
        /*
          或者
                PersonDemo pd = new PersonDemo(new Person() {
                @Override
                public void study() {
                    System.out.println("好好學習");
                }
            }).p.study();
         */
    }
}
           

六、匿名内部類面試題

/*
    interface Inter { void show(); }
	class Outer { //補齊代碼 }
	class OuterDemo {
	    public static void main(String[] args) {
		      Outer.method().show();
		  }
	}
要求在控制台輸出”HelloWorld”
分析:
    1、method()方法可以通過類名直接調用➡說明method()方法是靜态的
    2、調用完method()方法之後又可以去 .show() 方法,而show()方法恰好是接口Inter中的方法
        ➡說明method()方法是有傳回值的,而且傳回值類型是Inter類型
*/

interface Inter2 {
    void show();
}

class Outer8 {
    //1、推出第一個結論:method方法是靜态的

    //2、推出第二個結論:由于main方法中調用完method方法之後還能繼續調用方法
    //是以得出method方法是有傳回值的,由于show方法恰好是接口Inter2中的方法,是以傳回值類型
    //是接口Inter2類型
    public static Inter2 method(){

        //使用匿名内部類的形式
        return new Inter2() {
            @Override
            public void show() {
                System.out.println("HelloWorld");
            }
        };
    }
}

public class InnerClassDemo9 {
    public static void main(String[] args) {
        Outer8.method().show();
    }
}