Java內(nèi)部類深入解析
一、介紹
在java中,我們被允許在編寫一個(gè)類(外部類OuterClass)時(shí),在其內(nèi)部再嵌套一個(gè)類(嵌套類NestedClass),java將嵌套類分為兩種:非靜態(tài)內(nèi)部類(簡(jiǎn)稱內(nèi)部類) 和 靜態(tài)內(nèi)部類,如下所示
public class OuterClass { class InnerClass { } static class StaticInnerClass { } }
嵌套類作為外部類的一個(gè)成員,無論其是否為靜態(tài)內(nèi)部類,我們都可以對(duì)其添加任何的訪問修飾符(public、protected、private、default),如下所示
public class OuterClass { public class InnerClass { } protected static class StaticInnerClass { } }
非靜態(tài)內(nèi)部類對(duì)其外部類中的所有成員變量和方法都具有訪問權(quán),即便它被private修飾也可以。但是靜態(tài)內(nèi)部類對(duì)外部類中的成員變量和方法沒有訪問權(quán)。
二、為什么要使用內(nèi)部類
內(nèi)部類為我們提供了以下便利:
- 它對(duì)只在一個(gè)地方使用的類進(jìn)行邏輯分組
如果一個(gè)類A.java只對(duì)另一個(gè)類B.java有用,那么將它嵌入到那個(gè)類中并把兩個(gè)類放在一起豈不合理?即把A.java作為內(nèi)部類,而將B.java作為外部類。
- 它增加了封裝性
考慮兩個(gè)類A.java 和 B.java,其中B.java需要訪問A.java的被聲明為private的成員變量或方法。通過將類B.java隱藏在類A.java中,A.java的成員變量或方法可以被聲明為private,B.java也可以訪問它們。另外,B.java本身也可以對(duì)外界隱藏。
- 它使代碼更加可讀和可維護(hù)
將內(nèi)部類嵌套在外部類中使代碼更接近使用它的地方。
三、非靜態(tài)內(nèi)部類
非靜態(tài)內(nèi)部類有以下特點(diǎn):
- 對(duì)其外部類中的所有成員變量和方法都具有訪問權(quán),即便它被private修飾也可以。
- 不允許定義任何static修飾的成員變量和方法。
- 外部類必須通過非靜態(tài)內(nèi)部類的實(shí)例對(duì)象訪問其內(nèi)部的屬性和方法。
- 編譯后生成兩個(gè)class文件:外部類.class 和 外部類$內(nèi)部類.class。
當(dāng)我們實(shí)例化一個(gè)非靜態(tài)內(nèi)部類的對(duì)象時(shí),使用以下方法
- 在外部類中實(shí)例化內(nèi)部類
public class OuterClass { public void initInnerClass() { InnerClass innerClass = new InnerClass(); innerClass.innerClassMethod_1(); } class InnerClass { public void innerClassMethod_1() { System.out.println("調(diào)用內(nèi)部類方法"); } } }
- 在其他類中實(shí)例化內(nèi)部類
class InnerClassTest { public static void main(String[] args) { // 方式一:創(chuàng)建內(nèi)部類innerClass實(shí)例 OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass1 = outerClass.new InnerClass(); innerClass1.innerClassMethod_1(); // 方式二:創(chuàng)建內(nèi)部類innerClass實(shí)例 OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass(); innerClass2.innerClassMethod_1(); } }
非靜態(tài)內(nèi)部類還有兩種特殊的類型:局部?jī)?nèi)部類 和 匿名內(nèi)部類。后面細(xì)說。
四、靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類在行為上與其他類相似
- 對(duì)外部類成員的訪問只能通過外部類的實(shí)例對(duì)象。
- 編譯后生成兩個(gè)class文件:外部類.class 和 外部類$內(nèi)部類.class。
當(dāng)我們實(shí)例化一個(gè)靜態(tài)內(nèi)部類的對(duì)象時(shí),使用以下方法
在外部類中實(shí)例化內(nèi)部類
public class OuterClass { public void initStaticInnerClass() { StaticInnerClass staticInnerClass = new StaticInnerClass(); staticInnerClass.staticInnerClassMethod_1(); } static class StaticInnerClass { public void staticInnerClassMethod_1() { System.out.println("調(diào)用靜態(tài)內(nèi)部類方法"); } } }
在其他類中實(shí)例化內(nèi)部類
class InnerClassTest { public static void main(String[] args) { OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass(); staticInnerClass.staticInnerClassMethod_1(); } }
五、局部?jī)?nèi)部類
在一個(gè)代碼塊聲明的類叫局部?jī)?nèi)部類。
此處的代碼塊指任何{}內(nèi)部(如靜態(tài)代碼塊、方法、for循環(huán)、if塊)
- 局部?jī)?nèi)部類可聲明在任何代碼塊中
- 局部?jī)?nèi)部類內(nèi)部可以訪問其外部的成員變量,但該成員變量的值不允許被修改,因此我們需要使用final修飾。
- 不允許定義任何static靜態(tài)成員變量或方法
- 靜態(tài)方法中的局部?jī)?nèi)部類,只允許訪問外部類中的static靜態(tài)成員變量和方法。
- 不允許被static修飾
- 在{}代碼塊中不可以聲明接口interface。因?yàn)閕nterface接口天生就是靜態(tài)的。
- 局部?jī)?nèi)部類中僅允許在常量變量聲明中使用static。從java16開始允許不被final修飾
- 編譯后生成外部類.class 和 外部類+$+編號(hào)+內(nèi)部類.class。
創(chuàng)建局部?jī)?nèi)部類的方式如下:
public class OuterClass { public void localClassMethod() { for (int i=0; i<10; i++) { // for循環(huán)中的局部?jī)?nèi)部類 class LocalClassInFor { } } if (true) { // if代碼塊中的局部?jī)?nèi)部類 class LocalClassInIf { } } // 方法中的局部?jī)?nèi)部類 class LocalClass { } } }
六、匿名內(nèi)部類
在一個(gè)方法內(nèi)部聲明的類但沒有命名該類的名稱叫局部?jī)?nèi)部類。匿名類使您的代碼更加簡(jiǎn)潔。允許我們能夠同時(shí)聲明和實(shí)例化一個(gè)類。除了沒有名字之外,它們類似于局部?jī)?nèi)部類。如果只需要使用一次局部?jī)?nèi)部類,就使用它們。
- 本質(zhì)上是一個(gè)表達(dá)式。
- 實(shí)例化匿名內(nèi)部類需要借助一個(gè)父類 或 接口。
- 可以訪問外部類的成員變量和方法。與局部?jī)?nèi)部類相同。
- 不可以修改外部變量,但該變量的值不允許被修改,因此我們需要使用final修飾。與局部?jī)?nèi)部類相同。
- 與外部變量或方法重名時(shí),默認(rèn)采用就近原則訪問。與非靜態(tài)內(nèi)部類相同。
- 不可以聲明static靜態(tài)變量或方法。但可以聲明final static常量。
- 編譯后生成外部類.class 和 外部類+$+編號(hào).class。
使用匿名內(nèi)部類需要借助父類 或 接口實(shí)現(xiàn),其本質(zhì)相同,都是對(duì)方法的重寫。如下所示
使用父類
public class AnonymousOuterClass { class InnerClass { private String innerField = "field in InnerClass"; public void getField() { System.out.println(innerField); } } public void anonymousInnerMethod() { // 聲明并實(shí)例化匿名內(nèi)部類 InnerClass innerClass = new InnerClass() { private String innerField = "a"; @Override public void getField() { // 輸出匿名內(nèi)部類中的成員變量innerField System.out.println(innerField); // 輸出父類類中的成員變量innerField super.getField(); } }; innerClass.getField(); } } class AnonymousOuterClassTest { public static void main(String[] args) { AnonymousOuterClass anonymousOuterClass = new AnonymousOuterClass(); anonymousOuterClass.anonymousInnerMethod(); } }
使用接口
public class AnonymousOuterClass { interface InnerInterface { void getField(); } public void anonymousInnerInterfaceMethod() { InnerInterface innerInterface = new InnerInterface() { private String innerField = "field in inner interface"; @Override public void getField() { // 輸出匿名內(nèi)部類中的成員變量innerField System.out.println(innerField); } }; innerInterface.getField(); } } class AnonymousOuterClassTest { public static void main(String[] args) { AnonymousOuterClass anonymousOuterClass = new AnonymousOuterClass(); anonymousOuterClass.anonymousInnerInterfaceMethod(); } }
七、lambda表達(dá)式內(nèi)部類
使用lambda表達(dá)式內(nèi)部類的理由很簡(jiǎn)單:當(dāng)匿名內(nèi)部類的父類或接口中只有一個(gè)方法時(shí),其實(shí)現(xiàn)代碼最少也得五六行,為了使代碼簡(jiǎn)化,所以使用lambda表達(dá)式內(nèi)部類。
- 編譯后生成外部類.class。lambda表達(dá)式內(nèi)部類不會(huì)生成單獨(dú)的class文件。
- 其余特性與匿名內(nèi)部類相同。
除了實(shí)例化方式不同,lambda表達(dá)式內(nèi)部類和匿名內(nèi)部類其余使用限制完全一致。
public class LambdaOuterClass { interface InnerInterface { void sayHello(); } public void sayHello() { // lambda表達(dá)式內(nèi)部類 InnerInterface innerInterface = () -> System.out.println("hello world"); innerInterface.sayHello(); } } class LambdaOuterClassTest { public static void main(String[] args) { LambdaOuterClass lambdaOuterClass = new LambdaOuterClass(); lambdaOuterClass.sayHello(); } }
八、成員重名
無論是靜態(tài)內(nèi)部類還是非靜態(tài)內(nèi)部類,當(dāng)內(nèi)部類中與外部類具有相同名稱的成員變量的情況下,當(dāng)我們?cè)趦?nèi)部類中使用該變量時(shí),默認(rèn)采用就近原則的方式訪問該變量。如果需要訪問外部類中的重名變量時(shí),則需要通過OuterClass.this.field訪問,如下所示
public class OuterClass { private String sameNameField = "the same name field in OuterClass"; class InnerClass { private String sameNameField = "the same name field in InnerClass"; public void testSameNameField() { // 訪問內(nèi)部類的重名變量 System.out.println(sameNameField); // 訪問外部類的重名變量 System.out.println(OuterClass.this.sameNameField); } } }
九、序列化
java強(qiáng)烈建議不要序列化內(nèi)部類,包括局部?jī)?nèi)部類和匿名類。當(dāng)Java編譯器編譯某些結(jié)構(gòu)時(shí),比如內(nèi)部類,它創(chuàng)建合成結(jié)構(gòu),這些是在源代碼中沒有相應(yīng)構(gòu)造的類、方法、字段和其他構(gòu)造。合成結(jié)構(gòu)使Java編譯器能夠在不改變JVM的情況下實(shí)現(xiàn)新的Java語言特性。然而,合成構(gòu)造在不同的Java編譯器實(shí)現(xiàn)中可能有所不同,這意味著在不同的實(shí)現(xiàn)中,類文件也可能不同。因此,如果我們序列化一個(gè)內(nèi)部類,然后用不同的JRE實(shí)現(xiàn)反序列化它,可能會(huì)有兼容性問題。
十、如何選擇內(nèi)部類
非靜態(tài)內(nèi)部類
當(dāng)需要訪問外部類實(shí)例的非public修飾的成員變量和方法時(shí)。
靜態(tài)內(nèi)部類
當(dāng)不需要訪問外部類實(shí)例的成員變量和方法時(shí)。
局部?jī)?nèi)部類
當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)類的多個(gè)實(shí)例,請(qǐng)?jiān)L問其構(gòu)造函數(shù),或者引入一個(gè)新的命名類型(例如,因?yàn)槟院笮枰{(diào)用其他方法)。
匿名內(nèi)部類
當(dāng)我們需要同時(shí)聲明和實(shí)例化一個(gè)類,并且只需要使用一次局部?jī)?nèi)部類時(shí)。
lambda表達(dá)式內(nèi)部類
到此這篇關(guān)于Java內(nèi)部類深入解析的文章就介紹到這了,更多相關(guān)Java內(nèi)部類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)byte[]轉(zhuǎn)List的示例代碼
byte,即字節(jié),由8位的二進(jìn)制組成。在Java中,byte類型的數(shù)據(jù)是8位帶符號(hào)的二進(jìn)制數(shù)。List?是一個(gè)接口,它繼承于Collection的接口。它代表著有序的隊(duì)列。本文將介紹如何通過java實(shí)現(xiàn)byte[]轉(zhuǎn)List,需要的可以參考一下2022-01-01Spring中@Repository注解的作用和用法以及和@Mapper的區(qū)別詳析
這篇文章主要給大家介紹了關(guān)于Spring中@Repository注解的作用和用法以及和@Mapper的區(qū)別的相關(guān)資料,注解的作用是標(biāo)識(shí)一個(gè)類為數(shù)據(jù)訪問對(duì)象,并由Spring框架進(jìn)行實(shí)例化和管理,需要的朋友可以參考下2023-09-09springboot整合 xxl-job的項(xiàng)目實(shí)踐
XL-JOB是一個(gè)分布式任務(wù)調(diào)度平臺(tái),用于解決分布式系統(tǒng)中的任務(wù)調(diào)度和管理問題,它包括調(diào)度中心、執(zhí)行器和Web管理控制臺(tái),本文就來介紹一下springboot整合 xxl-job的項(xiàng)目實(shí)踐,感興趣的可以了解一下2024-09-09使用Java判定一個(gè)數(shù)值是否在指定的開閉區(qū)間范圍內(nèi)
這篇文章主要給大家介紹了關(guān)于使用Java判定一個(gè)數(shù)值是否在指定的開閉區(qū)間范圍內(nèi)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-09-09Java微信二次開發(fā)(一) Java微信請(qǐng)求驗(yàn)證功能
這篇文章主要為大家詳細(xì)介紹了Java微信二次開發(fā)第一篇,Java微信請(qǐng)求驗(yàn)證功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04有關(guān)tomcat內(nèi)存溢出的完美解決方法
下面小編就為大家?guī)硪黄嘘P(guān)tomcat內(nèi)存溢出的完美解決方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05java實(shí)現(xiàn)dijkstra最短路徑尋路算法
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)dijkstra最短路徑尋路算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01