【java】淺談java內(nèi)部類(二)

局部?jī)?nèi)部類

局部?jī)?nèi)部類是內(nèi)部類的第二種形式,它讓內(nèi)部類的“隱藏”得更深一層——寫在外部類的方法內(nèi)部,而不是處于和外部類方法平行的位置。
讓我們對(duì)上面成員內(nèi)部類處理的場(chǎng)景做些思考:我們的Inner內(nèi)部類僅僅只在outterPrint方法中使用了一次:

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

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

public class Outter {
  public void outterPrint () {// 外部類方法
    class LocalInner { // 局部?jī)?nèi)部類
      public void innerPrint () {   }
    } 
    LocalInner i = new LocalInner(); // 實(shí)例化局部?jī)?nèi)部類
    i.innerPrint();
  }
}

相比于成員內(nèi)部類,局部?jī)?nèi)部類多了一項(xiàng)能訪問的數(shù)據(jù),那就是局部變量(由外部類方法提供)

成員內(nèi)部類:外部類數(shù)據(jù),內(nèi)部類數(shù)據(jù)
局部?jī)?nèi)部類: 外部類數(shù)據(jù),內(nèi)部類數(shù)據(jù), 局部數(shù)據(jù)

具體示例如下:

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

Test.java:

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

結(jié)果輸出:

外部數(shù)據(jù)
內(nèi)部數(shù)據(jù)
局部數(shù)據(jù)

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

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

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

** (僅個(gè)人理解,如有不同的意見或者更好的理解歡迎在評(píng)論區(qū)討論)**

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

首先要說一下:

1.內(nèi)部類和外部類在編譯之后形式上是一樣的,不會(huì)有內(nèi)外之分
2.局部?jī)?nèi)部類對(duì)于使用的外部方法的值會(huì)用構(gòu)造函數(shù)做一個(gè)拷貝(編譯后)

例如對(duì)于下面outterPrint方法中的LocalInner

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

編譯之后大概長(zhǎng)這樣:

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

這里要注意的是:

1. 編譯后,LocalInner并非直接使用data,而是用構(gòu)造器拷貝一份后再使用
2. java是值傳遞的,所以包裹 LocalInner的外部方法outterPrint也會(huì)對(duì)傳入的data參數(shù)做一次拷貝(基本類型數(shù)據(jù)拷貝副本,對(duì)象等則拷貝引用)

OK,現(xiàn)在的情況是:

方法內(nèi)的局部類對(duì)data拷貝了兩次:外部方法outterPrint值傳遞時(shí)的拷貝,和LocalInner構(gòu)造函數(shù)的拷貝
方法內(nèi)除了局部類外的作用域只拷貝了data一次: 外部方法outterPrint值傳遞時(shí)的拷貝

拷貝兩次和拷貝一次,導(dǎo)致在outterPrint方法內(nèi)部, 局部類內(nèi)部的data和局部類外部的data是不同步的! 也即你在局部類內(nèi)部改了data不影響局部類外部的data,在局部類外部改了data也不影響局部類內(nèi)部的data(注意一個(gè)前提,值是基本類型的,如果是對(duì)象的話因?yàn)榭截惖氖且萌匀豢梢浴巴健保?/p>

圖示一:


圖示二:


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

【注意】所謂的不同步主要是針對(duì)基本類型來說的,如果是對(duì)象之類的話因?yàn)榭截惖氖且盟匀匀豢梢浴巴健?/strong>

如何突破必須用final的限制

我們上面說到,局部?jī)?nèi)部類所使用的方法形參必須用final修飾的限制。

例如

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

提示:

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

那么,如果我們有對(duì)該形參必須能修改的硬性需求怎么辦?
你可以通過一種有趣的方式繞開它:使用一個(gè)單元素?cái)?shù)組。因?yàn)橛胒inal修飾的基本類型的變量不允許修改值,但是卻允許修改final修飾的單元素?cái)?shù)組里的數(shù)組元素, 因?yàn)榇娣艛?shù)組的變量的值只是一個(gè)引用,我們修改數(shù)組元素的時(shí)候是不會(huì)修改引用指向的地址的,在這點(diǎn)上final并不會(huì)妨礙我們:
Outter.java

public class Outter {
  public void outterPrint (final String []  data) { 
    class LocalInner { 
      public void innerPrint () {
        data[0] = "堂而皇之地修改它!!";   // 修改數(shù)據(jù)
        System.out.print(data[0]);  // 輸出修改后的數(shù)據(jù)
      }
    } 
    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] = "我是數(shù)據(jù)";
    o.outterPrint(data);  // 修改數(shù)據(jù)并且輸出
  }
}

結(jié)果輸出:

堂而皇之地修改它??!

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

名內(nèi)部類

倘若我們?cè)侔丫植績(jī)?nèi)部類再深化一下, 那就是匿名內(nèi)部類

匿名內(nèi)部類的使用方式

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

讓我們看看下面這個(gè)例子:
Other.java:

public class Other {    }

Outter.java:

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

何謂之匿名?

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

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

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

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

同時(shí)要注意,我們?cè)?strong>使用匿名內(nèi)部類的方式,是在定義一個(gè)內(nèi)部類的同時(shí)實(shí)例化該內(nèi)部類:

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

匿名函數(shù)的作用

用匿名函數(shù)的作用在于在一些特定的場(chǎng)景下寫起來很簡(jiǎn)單,例如事件監(jiān)聽器:

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

避免了再創(chuàng)建另外一個(gè)類文件

講的有點(diǎn)亂, 對(duì)匿名內(nèi)部類做個(gè)總結(jié):

  1. 省略被定義的類的類名
  2. 必須結(jié)合超類或者接口使用,即 new [超類/接口] { /* 類體 */ }
  3. 在定義該匿名類的同時(shí)實(shí)例化該匿名類
  4. 在一些場(chǎng)景下能簡(jiǎn)化代碼

【注意】匿名類不能有構(gòu)造器, 因?yàn)闃?gòu)造器和類同名,而匿名類沒有類名,所以匿名類不能有構(gòu)造器

文章總結(jié)

我們使用內(nèi)部類的原因主要有三點(diǎn):

1.實(shí)現(xiàn)數(shù)據(jù)隱藏, 避免多余的可見性
2.自由訪問外部類的變量

  1. 在使用監(jiān)聽器等場(chǎng)景的時(shí)候使用匿名內(nèi)部類,避免增加的大量代碼
    關(guān)于成員內(nèi)部類, 方法局部類,匿名內(nèi)部類的關(guān)系
    從成員內(nèi)部類,方法局部類到匿名內(nèi)部類是一個(gè)不斷深入的關(guān)系, 成員內(nèi)部類進(jìn)一步隱藏可見性就成為了方法局部類, 方法局部類省去類名,并將類的定義和實(shí)例化操作合并到一起,就是匿名內(nèi)部類。因此,匿名內(nèi)部類沿襲了成員內(nèi)部類和方法局部類的基本特特
    內(nèi)部類的一些特殊的要求
    1.局部類不能用public或private訪問符進(jìn)行聲明
    2.局部類所使用的外部類方法的形參必須用final修飾
  2. 匿名內(nèi)部類不能有構(gòu)造器

歡迎加入學(xué)習(xí)交流群569772982,大家一起學(xué)習(xí)交流。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,769評(píng)論 18 399
  • 一:java概述:1,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境,java的開發(fā)工...
    ZaneInTheSun閱讀 2,690評(píng)論 0 11
  • Java 內(nèi)部類 分四種:成員內(nèi)部類、局部?jī)?nèi)部類、靜態(tài)內(nèi)部類和匿名內(nèi)部類。 1、成員內(nèi)部類: 即作為外部類的一個(gè)成...
    ikaroskun閱讀 1,252評(píng)論 0 13
  • 什么是版本庫(kù)呢?版本庫(kù)又名倉(cāng)庫(kù),可以理解為一個(gè)目錄,這個(gè)目錄的里面的所有文件都可以被git管理起來,這個(gè)目錄下的每...
    Simon_Zhang閱讀 525評(píng)論 0 0
  • 有效管理需要充分運(yùn)用每個(gè)人的長(zhǎng)處,共同完成任務(wù)。用人需先知人而擇人,其基礎(chǔ)為此人能做什么,即著眼于何所長(zhǎng),不能只想...
    牙印兒小co閱讀 364評(píng)論 0 0