日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

說到JAVA內部類,想必大家首先會想到比較常用的“匿名內部類”,但實際上,這只是內部類的其中一種使用方式而已內部類的使用方式實際上總共包括:成員內部類, 方法局部類,匿名內部類,下面,我就給大家來一一介紹:

為什么要使用內部類

有的時候你可能有這樣一種需求:對一個類(假設它為MyClass.java)創建一個和它相關的類(假設它是Part.java),但因為Part.java和MyClass之間的聯系“緊密”且“單一”,導致我們在這種情況下,不希望像下面這樣增加一個額外的兄弟類

├─MyClass      
└─Part

復制

而希望能將Part.java的數據隱藏在MyClass.java內部,于是這個時候內部類就堂而皇之地出現了

那么,這個不請自來的內部類到底給我們上述的局面造成了怎樣的改變呢? 讓我們來看看:

增加一個額外的兄弟類Part:

1. 對一些沒有關聯的類可見(如果protected則對同一包內類可見,如果public則對所有類可見)

2. 不能完全自由的訪問MyClass中的私有數據(必須經過訪問器方法)

3. 新增了一個java文件

使用內部類,將Part類的定義寫入MyClass內部

1. 可以減少多余的可見性,例如可把Part在MyClass內部定義為私有,這樣對同一包內其他類也不可見了

2. 內部類(Part)可以自由訪問外圍類的所有數據(MyClass),包括私有數據

3. 減少了一個java文件,使得類結構更簡潔

成員內部類

故名思議,成員內部類嘛~ 使用當然和成員變量很相似咯

你可以像

private String data

復制

這樣定義一個“平行的”成員內部類:

private class Inner

復制

具體看下面的例子:

Outter.java:

public class Outter {
  // 成員變量data
  private String data = "外部數據";
 
  //定義一個內部類
  private class Inner {
    public void innerPrint () {
      System.out.println(data);
    }
   } 
     
  // 外部類的方法, new一個內部類的實例并調用其innerPrint方法
  public void outterPrint () {
    Inner i = new Inner();
    i.innerPrint();
  }}

復制

Test.java:

public class Test {
  public static void main (String [] args) {
    Outter o = new Outter();
    o.outterPrint();
  }
}

復制

結果輸出:

外部數據

復制

看來這還是能達到我們預期的效果的:由于將Inner內部類設為private,它變得只對我們當前的外部類Outter類可見,我們成功地把它"隱藏"在了Outter類內部與此同時,它還自由地訪問到了Outter類的私有成員變量data

兩個this

雖然上面的例子看起來挺簡單的,但實際上內部類的作用機制還是比較復雜的。

首先要考慮的是“this”的問題,外部類和內部類各有一個this,關鍵在于內部類中我們如何對這兩個this作出區分:

我們假設上面的例子中的Inner類內部有一個方法fn:

private class Inner {
  public  void fn () {
    Outter.this // 指向Outter實例對象的this引用
    this  // 指向Inner實例對象的this引用
  }
} 

復制

在這個方法fn里,Outter.this是指向Outter實例對象的this的引用, 而this是指向Inner實例對象的this的引用

我們訪問類中成員變量有兩種方式: 隱式訪問(不加this)和顯式訪問(加this)

隱式訪問類中成員變量

讓我們對上面的Outter.java做一些改動,增加一行代碼:

public class Outter {
  // 成員變量data
  private String data = "外部數據"; 
  //定義一個內部類
  private class Inner {
    // 增加Inner類對data成員變量的聲明
    private String data = "內部數據"     public void innerPrint () {
      System.out.println(data);
    }
  } 
     
  // 外部類的方法, new一個內部類的實例并調用其innerPrint方法
  public void outterPrint () {
    Inner i = new Inner();
    i.innerPrint();
  }
}

復制

結果輸出:

內部數據

復制

如此可見,內部類內聲明的數據會覆蓋外部類的同名數據。或者說, 在上述例子中,對于data成員變量,它會首先在Inner的this中查找有無這個成員變量,然后沒有,那么就再在Outter.this中查找

顯式訪問類中成員變量

但有的時候我們希望既能訪問外部類的成員變量,同時也能訪問內部類的成員變量,這個時候我們就要使用到this了,但是如何區分內部類和外部類的this呢?你可以這樣:

以上述例子為例:

訪問外部類定義的成員變量:Outter.this.data

訪問內部類定義的成員變量:this.data

如下圖所示

public class Outter {
  // 外部類的成員變量data
  private String data = "外部數據"; 
  //定義一個內部類
  private class Inner {
    // 內部類的成員變量data
    private String data = "內部數據";
    public void innerPrint () {
      System.out.println(Outter.this.data);
      System.out.println(this.data);
    }
  } 
     
  // 外部類的方法, new一個內部類的實例并調用其innerPrint方法
  public void outterPrint () {
    Inner i = new Inner();
    i.innerPrint();
  }
}

復制

局部內部類

局部內部類是內部類的第二種形式,它讓內部類的“隱藏”得更深一層——寫在外部類的方法內部,而不是處于和外部類方法平行的位置。

讓我們對上面成員內部類處理的場景做些思考:我們的Inner內部類僅僅只在outterPrint方法中使用了一次:

public void outterPrint () {
  Inner i = new Inner();
  i.innerPrint();
}

復制

那么我們能不能把Inner內部類直接定義在outterPrint的內部呢?這樣的話,它就能更好地隱藏起來即使是類Outter中除outterPrint外的方法,也不能訪問到它:

現在的Outter的類看起來像這樣:

public class Outter {
  public void outterPrint () {// 外部類方法
    class LocalInner { // 局部內部類
      public void innerPrint () {   }
    } 
    LocalInner i = new LocalInner(); // 實例化局部內部類
    i.innerPrint();
  }
}

復制

相比于成員內部類,局部內部類多了一項能訪問的數據,那就是局部變量(由外部類方法提供)

成員內部類:外部類數據,內部類數據

局部內部類: 外部類數據,內部類數據, 局部數據

具體示例如下:

Outter.java

public class Outter {
  private String data = "外部數據";  // 外部類數據
  public void outterPrint (final String localData) { // 局部數據
    class LocalInner {
      private String data = "內部數據";  // 內部類數據
      public void innerPrint () {
        System.out.println(Outter.this.data);  // 打印外部類數據
        System.out.println(this.data);   //  打印內部類數據
        System.out.println(localData);  // 打印局部數據
      }
    } 
    LocalInner i = new LocalInner();
    i.innerPrint();
  }
}

復制

Test.java:

public class Test {
  public static void main (String [] args) {
    Outter o = new Outter();
    o.outterPrint("局部數據");
  }
}

復制

結果輸出:

外部數據
內部數據
局部數據

復制

局部類所使用的外部類方法的形參必須用final修飾

這里要注意一點, 局部類所使用的外部類方法的形參必須用final修飾,否則會編譯不通過,也就是說傳入后不許改變

為什么這個方法形參一定要用final修飾?

(僅個人理解,如有不同的意見或者更好的理解歡迎在評論區討論)

如果不用final修飾會怎樣? 且聽我慢慢道來:

首先要說一下:

1.內部類和外部類在編譯之后形式上是一樣的,不會有內外之分

2.局部內部類對于使用的外部方法的值會用構造函數做一個拷貝(編譯后)

例如對于下面outterPrint方法中的LocalInner

public void outterPrint (final String data) {
  class LocalInner {
    public void innerPrint () {
    // 使用 data
    }
  }
}

復制

編譯之后大概長這樣:

public class Outter$LocalInner{ 
  public LocalInner(String data){
    this.LocalInner$data = data; // 對于使用的data做了一次拷貝
  }
  public void innerPrint (){ /* 使用 data */ }
}

復制

這里要注意的是:

1. 編譯后,LocalInner并非直接使用data,而是用構造器拷貝一份后再使用

2. java是值傳遞的,所以包裹 LocalInner的外部方法outterPrint也會對傳入的data參數做一次拷貝(基本類型數據拷貝副本,對象等則拷貝引用)

OK,現在的情況是:

方法內的局部類對data拷貝了兩次:外部方法outterPrint值傳遞時的拷貝,和LocalInner構造函數的拷貝

方法內除了局部類外的作用域只拷貝了data一次: 外部方法outterPrint值傳遞時的拷貝

拷貝兩次和拷貝一次,導致在outterPrint方法內部, 局部類內部的data和局部類外部的data是不同步的! 也即你在局部類內部改了data不影響局部類外部的data,在局部類外部改了data也不影響局部類內部的data(注意一個前提,值是基本類型的,如果是對象的話因為拷貝的是引用仍然可以“同步”)

圖示一:

圖示二:

 

于是java說: 哎呀媽呀, 這都data都不同步了, 要是讓你修改這還了得!!! 于是就強行要求我們加上final

【注意】所謂的不同步主要是針對基本類型來說的,如果是對象之類的話因為拷貝的是引用所以仍然可以“同步”

如何突破必須用final的限制

我們上面說到,局部內部類所使用的方法形參必須用final修飾的限制。

例如

public void outterPrint (String data) {// 沒加上final
  class LocalInner { 
    public void changeData () {
      data = "我想修改data的值";  // 在這一行編譯報錯
    }
   } 
}

復制

提示:

Cannot refer to a non-final variable data inside an inner class defined in a different method

復制

那么,如果我們有對該形參必須能修改的硬性需求怎么辦?

你可以通過一種有趣的方式繞開它:使用一個單元素數組。因為用final修飾的基本類型的變量不允許修改值,但是卻允許修改final修飾的單元素數組里的數組元素, 因為存放數組的變量的值只是一個引用,我們修改數組元素的時候是不會修改引用指向的地址的,在這點上final并不會妨礙我們:

Outter.java

public class Outter {
  public void outterPrint (final String []  data) { 
    class LocalInner { 
      public void innerPrint () {
        data[0] = "堂而皇之地修改它!!";   // 修改數據
        System.out.print(data[0]);  // 輸出修改后的數據
      }
    } 
    LocalInner i = new LocalInner();
    i.innerPrint();
  }
}

復制

Test.java:

public class Test {
  public static void main (String [] args) {
    Outter o = new Outter();
    String [] data = new String [1];
    data[0] = "我是數據";
    o.outterPrint(data);  // 修改數據并且輸出
  }
}

復制

結果輸出:

堂而皇之地修改它!!

復制

【注意】局部類不能用public或private訪問符進行聲明!!

匿名內部類

倘若我們再把局部內部類再深化一下, 那就是匿名內部類

匿名內部類的使用方式

new [超類/接口] {   /* 類體 */   }

復制

讓我們看看下面這個例子:

Other.java:

public class Other {    }

復制

Outter.java:

public class Outter {
  public void outterPrint (String data) { 
    Other o = new Other() {  }; // 匿名內部類
  }
}

復制

何謂之匿名?

誒,不是說好的匿名嗎? 那么為什么還有個Other的類名呢?”

Other o = new Other() {  /* 匿名內部類的類體 */   };

復制

實際上,這里的Other并不是我們的匿名內部類,而是我們匿名內部類的超類,上面一行代碼其實相當于(用成員內部類來表示的話)

// annoymous翻譯為匿名
public class Outter {
  private class annoymous extends Other{  }  
  public void outterPrint () { 
    Other a = new annoymous();
  }
}

復制

同時要注意,我們在使用匿名內部類的方式,是在定義一個內部類的同時實例化該內部類:

new Other() {  /* 匿名內部類的類體 */  };  // new操作和定義類的代碼是緊緊結合在一起的

復制

匿名函數的作用

用匿名函數的作用在于在一些特定的場景下寫起來很簡單,例如事件監聽器:

ActionListener listener = new ActionListener() { 
  public void actionPerformed(ActionEvent e) {   }
};

復制

避免了再創建另外一個類文件

講的有點亂, 對匿名內部類做個總結:

1. 省略被定義的類的類名

2. 必須結合超類或者接口使用,即 new [超類/接口] { /* 類體 */ }

3. 在定義該匿名類的同時實例化該匿名類

4. 在一些場景下能簡化代碼

【注意】匿名類不能有構造器, 因為構造器和類同名,而匿名類沒有類名,所以匿名類不能有構造器

文章總結

我們使用內部類的原因主要有三點:

1.實現數據隱藏, 避免多余的可見性

2.自由訪問外部類的變量

3. 在使用監聽器等場景的時候使用匿名內部類,避免增加的大量代碼

關于成員內部類, 方法局部類,匿名內部類的關系

從成員內部類,方法局部類到匿名內部類是一個不斷深入的關系, 成員內部類進一步隱藏可見性就成為了方法局部類, 方法局部類省去類名,并將類的定義和實例化操作合并到一起,就是匿名內部類。因此,匿名內部類沿襲了成員內部類和方法局部類的基本特特性

內部類的一些特殊的要求

1.局部類不能用public或private訪問符進行聲明

2.局部類所使用的外部類方法的形參必須用final修飾

3. 匿名內部類不能有構造器

參考資料:

《java核心技術 卷1》—— Cay S. Horstmann, Gary Cornell

分享到:
標簽:java
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定