Java中內部類到底有什麼用?

2020-12-11 Java架構師日記

java中內部類種類較多,語法比較複雜,用法也不盡相同。

概括下來,可以分類為以下五種內部類。

內部類嵌套內部類局部內部類接口內部類匿名內部類本篇文章只對實際項目開發中用的較多的,普通內部類與匿名內部類做一定介紹。其他三種若有興趣請自行通過谷歌或書籍進行了解。

內部類

首先通過一個簡單的小示例,來看看內部類的語法吧。

import java.util.HashMap;public class Parcell { private HashMap<String, String> testMap = new HashMap<String, String>(); class Contents { // 返回一個外部類的引用. public Parcell ParcellRef = Parcell.this; } class Destination { public void putSomethingInMap() { testMap.put("hello", "world"); System.out.println(testMap.get("hello")); } } public Destination to() { return new Destination(); } public Contents contents() { return new Contents(); } public void ship(String dest) { Contents c = new Contents(); Destination d = new Destination(); } public static void main(String[] args) { Parcell p = new Parcell(); Parcell.Contents c = p.contents(); Parcell.Destination d = p.to(); d.putSomethingInMap(); Parcell.Contents c1 = p.new Contents(); }}內部類的語法和相信大家都很熟悉。

在這裡我再自作主張的為大家概括一下

普通內部類持有一個指向外部類的引用。要創建普通內部類,一定要先創建外部類。普通內部類就像人體的心臟一樣,能夠隨意訪問外部類的任意成員變量。在內部類中可以通過「外部類類名.this」的方式返回一個指向外部類實例的引用.如Parcell.this在外部類的static方法中若要創建內部類對象,則需要通過「外部類類名.new XXX()」的方式來創建。普通內部類中不能擁有靜態成員變量。靜態內部類中可以擁有靜態成員變量。也可以擁有非靜態成員變量。但是靜態內部類不能訪問外部類中非靜態的成員變量。而普通內部類可以訪問外部類的靜態成員變量。為什麼static方法中需要p.new XXX()的方式而非static方法中我們直接new 內部類名 就可以創建一個對象了呢?如果你有這樣的疑問請再看看第一條,一定可以想明白的。

普通內部類的語法大致就是這樣了。

那麼,回到我們主題。

內部類到底有什麼用?我們在實際項目中,應該如何使用內部類呢?

內部類的用處或者說用法,歸根結底,主要就是一點:

: 「內部類,主要是設計出來用來解決java中所『缺少』的,多重繼承的概念的。」

什麼?難道java不是靠接口來實現多重繼承的嗎?我認為,這種說法對,也不對。下面我們來看這樣一個例子。

你正在參與一項代號X的星際飛船項目。

宇航局的人希望飛船上的綜合機器人能夠完成兩個工作。一是作為嚮導,能夠供人們查閱信息。二是作為修理機器人,完成一些簡單的飛船日常維護工作。

作為軟體工程師,你被要求編寫一段代碼來實現這兩個功能。

好消息是,嚮導部分的功能與飛船修理維護的功能,已經由你的同事們完成了!太好了,我只需要調用他們提供給我們的接口就大功告成了!

壞消息是,同事們編寫的嚮導功能與飛船修理功能的接口,竟然都叫做work!

這可傷腦筋了,應該怎麼辦呢?

public class Guider { public void work(String name) { System.out.println("歡迎光臨" + name + ",請查閱飛船信息"); }}-------------------------------------------------------------------public class Repairer { public void work (String name) { System.out.println("你好" + name + ",開始準備對飛船進行維護."); }}-------------------------------------------------------------------public class SpacecraftRobot extends Guider { public void doGuidWork(String name) { // 調用guider的work方法 work(name); } public void doRepairWork(String name) { // 返回內部類引用,調用內部類實例的work方法。 new repairerRobot().doRepairWork(name); } public class repairerRobot extends Repairer { public void doRepairWork(String name) { work(name); } }}太棒了。通過使用內部類與「多重繼承」,我們實現了這個功能。現在這個綜合機器人能夠正常工作了!

對於用戶來說,只需要走到機器人面前,告訴機器人你想要doGuidWork還是doRepairWork,它就能夠幫你幹活兒了。內部類的代碼對用戶,對外界徹底隱藏了起來,用戶唯一能夠獲得的信息就是這兩個方法而已。

匿名內部類

綜合機器人原型機試做成功後,新的工作來了!

我們需要對原型機進行量產。以滿足每艘星際飛船的需要。

現在我們要編寫一間生產綜合機器人的工廠。每當我們訪問一次工廠,就能夠從工廠中提取出一臺嶄新的綜合機器人。聰明的你想到了用工廠設計模式來解決這個問題!但是由於有了內部類,所以我們的工廠,稍稍顯得有點不同

// 機器人工廠接口。通過getSpaceCraftRobot方法對外提供機器人public interface SpaceCraftRobotFactory { SpacecraftRobot getSpaceCraftRobot();}-------------------------------------------------------------------public class ProduceSpaceCraftRobot { // 再也不用顯示的創建工廠類的對象了! private ProduceSpaceCraftRobot() { } // 通過匿名內部類,創建工廠對象!將工廠封裝到了內部。不對外界暴露 public static SpaceCraftRobotFactory produceRobot = new SpaceCraftRobotFactory () { public SpacecraftRobot getSpaceCraftRobot() { return new SpacecraftRobot(); } };}-------------------------------------------------------------------// 客戶public class Consumer { public static void main(String[] args) { // 客戶來提取機器人了. SpacecraftRobot x1 = ProduceSpaceCraftRobot.produceRobot.getSpaceCraftRobot(); x1.doGuidWork("lch"); x1.doRepairWork("lch"); }}通過創建匿名內部類,我們使傳統的工廠設計模式優雅了許多!再也不用在外部編寫new xxxFactory()這樣醜陋,多餘的代碼了。

現在的工廠被匿名內部類隱藏了起來。客戶只需要關心有沒有拿到稱心如意的機器人。不應該,不需要關心工廠的名字,也不需要知道工廠是幹嘛的。

真棒,你順利完成了宇航局交給你的任務。

恭喜你,你是這個星球的英雄。

Java中的閉包,閉包與內部類的關係

作為一個程式設計師,即使你從來沒有使用過,你也應該聽說過閉包與回調。

要從java,特別是j2ee的方向入手去講解閉包與回調,會比較困難。

所以我們首先從python來入手,來了解閉包與回調到底是什麼。

python是一門優秀的解釋性語言。你應該掌握他。

下面我們來看一看,標準的回調,在Python中是什麼樣子的。

#定義一個返回sum函數的名叫base_sum函數的函數def base_sum(a,b): #在base_sum中定義一個sum()函數用來計算base_sum(a,b)的形參之合 def sum(): #返回a+b的值 return a + b #返回定義的sum函數 return sum#調用base_sum返回函數sum(),可以理解為返回了一個函數指針return_method = base_sum(1,2)#列印出返回的函數對象print(return_method)#通過指針回調函數對象,返回a與b的合print(return_method())----------<function base_sum.<locals>.sum at 0x1018f3c80>3對於java程式設計師來說,在一個函數中定義另外一個函數也許會比較燒腦。

你可以試著這樣去理解他:

「首先你需要了解的是,函數也需要佔據內存空間,所以函數在內存中也是有地址的。在c語言中,函數名就代表這個函數的地址。

如果你有過c語言的編程經驗,你就應該知道在一個函數中,返回一個指針是一件很容易的事情。

所以,對於以上這段python代碼,你可以嘗試把它理解為:

base_sum()函數中定義了一個指向sum()函數的指針,並且這個指針作為base_sum()的返回值。」

好了,現在我們根據上面的例子,來「定義一下閉包」。

: 調用外部函數,返回一個持有外部函數變量,參數引用的內部函數對象的程序結構,我們就稱它為「閉包」。

遺憾的是,java中沒有為我們顯示的提供指針供我們操作,也沒有提供類似python,javascrpit中的函數定義的語法,那麼我們應該如何實現閉包呢?

不妨還是通過綜合機器人來解答這個疑問吧。這一次,讓我們稍稍修改一下綜合機器人的代碼如下:

public class SpacecraftRobot extends Guider { // 外部類的成員變量 private String name; public SpacecraftRobot(String name) { this.name = name; } public class repairerRobot extends Repairer { public void doRepairWork() { // 內部類持有外部類的引用,訪問外部類的成員變量name。 work(name); } } public void doGuidWork() { // 調用guider的work方法 work(name); } public void doRepairWork() { // 返回一個持有外部類變量引用的內部類的對象,然後調用這個對象,實現具體的業務邏輯. new repairerRobot().doRepairWork(); }}通過對java內部類的合理利用,我們「模擬」出了一個閉包的程序結構。

該程序通過調用外部類對象,從而返回了一個持有外部類對象變量引用的內部類對象。當我們再次調用內部類對象的某個方法時,我們實現了具體的業務邏輯。

總結

內部類通常用來解決「多重繼承」的問題。當你希望隱藏一個類的實現,減少工程中.java文件數量,或者這個類不想被擴展時,你可以通過匿名內部類來創建一個類的對象。java雖然無法直接在語法層面上支持閉包,但是可以通過內部類來模擬一個閉包的程序結構。

相關焦點

  • Java提高篇——詳解內部類
    可以將一個類的定義放在另一個類的定義內部,這就是內部類。內部類是一個非常有用的特性但又比較難理解使用的特性(鄙人到現在都沒有怎麼使用過內部類,對內部類也只是略知一二)。第一次見面內部類我們從外面看是非常容易理解的,無非就是在一個類的內部在定義一個類。
  • Java內部類新解,你沒有見過的船新版本
    基礎Java支持類中嵌套類,稱之為nested class。嵌套的層數沒有限制,但實際中一般最多用兩層。根據內部類是否有static修飾,分為 static nested class和 non-static nested class。
  • java序列化反序列化中serialVersionUID到底有什麼用
    前言:在回答上面的問題之前,首先要知道什麼是序列化、反序列化、用途是什麼、實現的必要條件。序列化\反序列化:java序列化是指把java對象轉換為字節序列的過程,而java反序列化是指把字節序列恢復為java對象的過程。
  • Java基礎知識中的類
    private:私有類型,只允許在類內部的方法中使用,若從外部訪問,必須通過構造函數間接進行。protected:受保護類型,子類訪問受到限制。數據類型包括基本類型以及用戶自定義的擴展類型。>二、類的單繼承性Java程式語言中允許用extends關鍵字從一個類擴展出一個新類,新類繼承超類的成員變量和方法,並可以覆蓋方法。
  • java中的持有對方引用是什麼意思?有什麼用嗎?
    持有對方引用比如 A類有: h、j、 k, 3種方法,B類有 h 1種方法,但我B類必須要用到 A 類的 k方法怎麼辦呢?那麼下面就要引入引用了!我在 B 類中創建個A類的引用,例如 如果在同一包下就不用導包了import 包名.Apublic class B {A a=null; 此a就是B類中 對A的引用a=new A(); 用此引用創建了A類的實例}
  • 概述Java中的數據結構是什麼及其內部實現原理
    那麼數組是什麼呢,數組是一個對象,準確的說我們將對象進行分類,具有特定功能的對象就被成為數據結構或者容器,集合等,數組就是其中之一。有一個人說過程序就是算法與數據結構,這句話大體上能夠描述程序。我們從數組的特性來慢慢延伸到集合。
  • Java中Thread.start和Thread.run是什麼?有什麼區別
    在Java的多線程概念中,start()和run()是兩個最重要的方法。那麼它們之間的區別是什麼?下面本篇文章就來帶大家認識一下start()和run()方法,介紹它們之間的區別,希望對大家有所幫助。一:Java中Thread.start和Thread.run是什麼?1.Java中的start()方法是什麼?
  • 論Java中的抽象類與接口
    接口定義特殊的「抽象類」——接口(interface):比抽象類更加抽象的是接口,在接口中所有的方法都是抽象的。就不能像上面的抽象類一樣還可以有普通方法。>抽象類可以有普通方法。和類繼承相似,子接口擴展某個父接口,將會獲得父接口裡定義的所有抽象方法、常量Field、內部類和枚舉類定義。
  • Java中線程池,你真的會用嗎?
    在文中有這樣一段描述:可以通過Executors靜態工廠構建線程池,但一般不建議這樣使用。關於這個問題,在那篇文章中並沒有深入的展開。作者之所以這麼說,是因為這種創建線程池的方式有很大的隱患,稍有不慎就有可能導致線上故障。本文我們就來圍繞這個問題來分析一下為什麼JDK自身提供的構建線程池的方式並不建議使用?
  • 阿里內部員工,排查Java問題常用的工具單
    utm_content=m_10360這是一篇來源於阿里內部技術論壇的文章,原文在阿里內部獲得一致好評。作者已經把這篇文章開放到雲棲社區中供外網訪問。文章內容做了部分刪減,主要刪減掉了其中只有阿里內部才能使用的工具的介紹,並刪減掉部分只有通過阿里內網才能訪問到的連結。
  • Java匿名、靜態、非靜態、方法區內部類,忘記了,一塊學習下
    01 內部類的定義在java中內部類是將一個類定義在另一個類的方法或者類中,這樣的類我們稱它為內部類。內部類可以分為四種:局部內部類、成員內部類、匿名內部類、靜態內部類,下面我們逐一介紹這四種內部類。02局部內部類存在方法中的內部類叫局部內部類。
  • 一文讀懂JAVA類文件結構
    有些人說了類文件結構我們知道或者不知道沒有什麼作用,如果你持有這種觀點,我覺得需要矯正一下,類文件結構是沒有多大的作用,但是它是我們代碼和java虛擬機之間的橋梁,如果研究透了類文件結構,那麼會對我們研究虛擬機的運行起到至關重要的作用,同樣對於我們研究jvm內存調優能夠帶來很大的幫助。
  • 一個 Java 對象到底有多大?
    作者:李小武來源:http://blog.lichengwu.cn/編寫Java代碼的時候,大多數情況下,我們很少關注一個Java對象究竟有多大(佔據多少內存),更多的是關注業務與邏輯。但是殊不知,在我們不經意間,大量的內存被無形地浪費了。一個Java對象到底有多大?
  • Java編程中常見的異常
    java.lang.arithmeticexception     這個異常的解釋是"數學運算異常",比如程序中出現了除以零這樣的運算就會出這樣的異常,對這種異常,大家就要好好檢查一下自己程序中涉及到數學運算的地方,公式是不是有不妥了。 4.
  • java基礎入門-day10-內部類概述
    class A {class B {class C {}}private A a;public B(A a) {this.a = a;1 什麼是內部類把一個類定義在另一個類的內部,那麼這個類就是內部類了。
  • 一文了解Java的內部類
    內部類也是一個單獨類,既然是類就有面向對象的特性,它就可以進行繼承操作,並且內部類的繼承操作對於外部類是否發生過繼承是沒有影響的。這樣內部類和外部類就共同實現了多繼承操作(如果內部類中還有內部類,繼承的個數會被擴大下去)。3.可以作為抽象類、接口的匿名實現。
  • 你真的了解java類加載器嗎?
    Java默認的類加載器通常可以分為三種:1.啟動類加載器(BootstrapClassLoader):jvm內部C++實現,無法獲取2.擴展類加載器(ExtClassLoader)3.應用程式類加載器(AppClassLoader)加載路徑各不同:1.BootstrapClassLoader加載Java核心API,即\jre\lib
  • java類加載的過程概述
    任何類被使用時系統都會建立一個Class對象。2. 連接:(1)驗證是否有正確的內部結構,並和其他類協調一致。(2)準備負責為類的靜態成員分配內存,並設置默認初始化值。(3)解析將類的二進位數據中的符號引用替換為直接。
  • java.lang.String 的 + 號操作到底做了什麼?
    對於String的考察還是挺頻繁的,大致考察以下幾個知識點:雖然面試中大體答對了,但是今天早上微信群裡的一個問題我卻答不上來,這個問題是這樣的:String str3 = "what";String str4 = str3 + " a nice day";//運行時, + 相當於 new,所以堆中會有 "what a nice day"
  • java面試題總結:java的接口類和抽象類的特點
    java的接口類和抽象類的特點java中抽象類的特點: