該項(xiàng)目源碼地址:https://github.com/ggb2312/JavaNotes/tree/master/java-basic
1. 簡(jiǎn)介
可以將一個(gè)類的定義放在另一個(gè)類的定義內(nèi)部,這就是內(nèi)部類键兜。
一般格式為:
public class Zoo{
...
class Panda{
}
}
內(nèi)部類是一種非常有用的特性位迂,它允許你把一些邏輯相關(guān)的類組織在一起伍伤,并控制位于內(nèi)部的類的可見性姻几。內(nèi)部類與組合是完全不同的概念必搞。
內(nèi)部類提供一種代碼隱藏機(jī)制:“將類置于其他類的內(nèi)部”绩脆,同時(shí)內(nèi)部類也了解外部類刘莹,并能與之通信。
2. 內(nèi)部類實(shí)例
內(nèi)部類有以下四種形式
- 成員內(nèi)部類
- 局部?jī)?nèi)部類
- 靜態(tài)內(nèi)部類
- 匿名內(nèi)部類
成員內(nèi)部類和靜態(tài)內(nèi)部類可以擁有private訪問(wèn)權(quán)限目代、protected訪問(wèn)權(quán)限屈梁、public訪問(wèn)權(quán)限及包訪問(wèn)權(quán)限嗤练。比如上面的例子,如果成員內(nèi)部類Inner用private修飾在讶,則只能在外部類的內(nèi)部訪問(wèn)煞抬,如果用public修飾,則任何地方都能訪問(wèn)构哺;如果用protected修飾此疹,則只能在同一個(gè)包下或者繼承外部類的情況下訪問(wèn);如果是默認(rèn)訪問(wèn)權(quán)限遮婶,則只能在同一個(gè)包下訪問(wèn)。這一點(diǎn)和外部類有一點(diǎn)不一樣湖笨,外部類只能被public和包訪問(wèn)兩種權(quán)限修飾旗扑。我個(gè)人是這么理解的,由于成員內(nèi)部類看起來(lái)像是外部類的一個(gè)成員慈省,所以可以像類的成員一樣擁有多種權(quán)限修飾臀防。
下面我們通過(guò)幾個(gè)實(shí)例來(lái)看看內(nèi)部類具體長(zhǎng)什么樣。
2.1 成員內(nèi)部類
成員內(nèi)部類和成員變量一樣边败,屬于類的全局成員袱衷。
成員內(nèi)部類可以擁有private訪問(wèn)權(quán)限、protected訪問(wèn)權(quán)限笑窜、public訪問(wèn)權(quán)限及包訪問(wèn)權(quán)限致燥。
一般格式:
public class OuterClass { //外部類
int id; // 成員變量
class InnerClass { //成員內(nèi)部類
}
}
實(shí)例:
一個(gè)類作為另一個(gè)類的成員(不是作為成員變量,作為成員變量的話就成組合模式了)排截,同時(shí)成員內(nèi)部類可以無(wú)條件的使用外部類的一切靜態(tài)變量嫌蚤、成員變量、靜態(tài)方法断傲、成員方法脱吱,用于內(nèi)部類和外部類通信。
public class OuterClass {
private String name;
public OuterClass(String name) {
this.name = name;
}
// 成員內(nèi)部類认罩,類比對(duì)象的成員變量
private class InnerClass {
int innerPrice;
public InnerClass(int innerPrice) {
System.out.println("成員內(nèi)部類~類比對(duì)象的成員變量");
this.innerPrice = innerPrice;
}
public void print() {
helloInnerClass();
System.out.println("出售:" + name + " 單價(jià):" + innerPrice);
}
}
public void helloInnerClass() {
System.out.println("我是外部類的helloInnerClass方法箱蝠,內(nèi)部類你可以調(diào)用我");
}
public static void main(String[] args) {
OuterClass sample = new OuterClass("香蕉");
InnerClass inner = sample.new InnerClass(20);
inner.print();
}
}
運(yùn)行結(jié)果:
成員內(nèi)部類~類比對(duì)象的成員變量
我是外部類,內(nèi)部類你可以調(diào)用我
出售:香蕉 單價(jià):20
2.2 局部?jī)?nèi)部類
局部?jī)?nèi)部類和局部變量一樣垦垂,都是在方法內(nèi)定義的宦搬,其有效范圍只在方法內(nèi)有效。
一般格式:
public class OuterClass { //外部類
public void print(){ // print方法
class InnerClass { //局部?jī)?nèi)部類
}
}
}
實(shí)例:
局部?jī)?nèi)部類可以無(wú)條件的使用外部類的一切靜態(tài)變量乔外、成員變量床三、靜態(tài)方法、成員方法杨幼,用于內(nèi)部類和外部類通信撇簿。
public class OuterClass {
private String name;
public OuterClass(String name) {
this.name = name;
}
public void helloInnerClass() {
System.out.println("我是外部類的helloInnerClass方法聂渊,內(nèi)部類你可以調(diào)用我");
}
public void print(int price) {
// 局部?jī)?nèi)部類,類比方法內(nèi)的局部變量
class InnerClass {
int innerPrice;
public InnerClass(int innerPrice) {
System.out.println("局部?jī)?nèi)部類~類比方法內(nèi)的局部變量");
this.innerPrice = innerPrice;
}
public void sell() {
helloInnerClass();
System.out.println("出售:" + name + " 單價(jià):" + innerPrice);
}
}
InnerClass apple = new InnerClass(price);
apple.sell();
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass("蘋果");
outerClass.print(10);
}
}
運(yùn)行結(jié)果:
局部?jī)?nèi)部類~類比方法內(nèi)的局部變量
我是外部類的helloInnerClass方法四瘫,內(nèi)部類你可以調(diào)用我
出售:蘋果 單價(jià):10
2.3 靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類和靜態(tài)變量類似汉嗽,它都是使用static
關(guān)鍵字修飾。
靜態(tài)內(nèi)部類可以擁有private訪問(wèn)權(quán)限找蜜、protected訪問(wèn)權(quán)限饼暑、public訪問(wèn)權(quán)限及包訪問(wèn)權(quán)限。
一般格式:
public class OuterClass { //外部類
class InnerClass { //靜態(tài)內(nèi)部類
}
}
實(shí)例:
靜態(tài)內(nèi)部類只能使用外部類的一切成員變量洗做、成員方法弓叛,用于內(nèi)部類和外部類通信。
public class OuterClass {
private static String name="靜態(tài)內(nèi)部類";
// 靜態(tài)內(nèi)部類诚纸,類比類的靜態(tài)變量
private static class InnerClass {
public void print() {
System.out.println(name+"~類比類的的靜態(tài)變量");
}
}
public static void main(String[] args) {
OuterClass.InnerClass sample = new OuterClass.InnerClass();
sample.print();
}
}
運(yùn)行結(jié)果:
靜態(tài)內(nèi)部類~類比類的的靜態(tài)變量
2.4 匿名內(nèi)部類
匿名內(nèi)部類就是沒(méi)有名字的內(nèi)部類撰筷,其名稱由Java編譯器給出,一般是形如:“外部類名稱+$+匿名類順序”畦徘,沒(méi)有名稱也就是其他地方就不能引用毕籽。其必須要實(shí)現(xiàn)一個(gè)接口或者繼承一個(gè)父類,主要是用來(lái)簡(jiǎn)化代碼井辆,常常用于Swing程序設(shè)計(jì)中的事件監(jiān)聽處理关筒。
一般格式:
public class OuterClass { //外部類
public void print(){ // print方法
new InnerClass(){
...
};
}
}
實(shí)例:
匿名內(nèi)部類只能使用外部類的一切成員變量、成員方法杯缺,用于內(nèi)部類和外部類通信蒸播。
定義一個(gè)接口
public interface InnerClass {
// 接口方法默認(rèn)public
void print();
}
定義一個(gè)外部類
public class OuterClass {
public static void print(InnerClass innerClass) {
innerClass.print();
}
public static void main(String[] args) {
OuterClass.print(new InnerClass() {
@Override
public void print() {
System.out.println("匿名內(nèi)部類~由于沒(méi)有引用,每次新創(chuàng)建的萍肆,在Minor GC時(shí)被清除");
}
});
}
}
運(yùn)行結(jié)果:
匿名內(nèi)部類~由于沒(méi)有引用廉赔,每次新創(chuàng)建的,在Minor GC時(shí)被清除
匿名內(nèi)部類的名稱:外部類名稱+$+匿名類順序
使用JDK8提供的lambda表示替換匿名內(nèi)部類
public class OuterClass {
public static void print(InnerClass innerClass) {
innerClass.print();
}
public static void main(String[] args) {
OuterClass.print(() -> System.out.println("匿名內(nèi)部類~由于沒(méi)有引用匾鸥,每次新創(chuàng)建的蜡塌,在Minor GC時(shí)被清除"));
}
}
匿名內(nèi)部類在Swing中的實(shí)例:
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class test extends JFrame{
JPasswordField passwordField;
JTextField textField;
test(){
super();
setTitle("QQ");
setBounds(100, 100, 380, 280);
getContentPane().setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textField = new JTextField("密碼");
textField.setBounds(100, 155, 120, 21);
getContentPane().add(textField);
// new MouseAdapter()使用匿名內(nèi)部類
textField.addMouseListener(new MouseAdapter(){
@Override
public void mouseClicked(MouseEvent e) {
getContentPane().remove(textField);
passwordField = new JPasswordField();
passwordField.setBounds(100, 155, 120, 21);
getContentPane().add(passwordField);
}
});
}
public static void main(String[] args) {
new test().setVisible(true);;
}
}
在給textField
添加鼠標(biāo)監(jiān)聽事件的時(shí)候,使用了new MouseAdapter(){}
匿名內(nèi)部類作為方法的參數(shù)勿负。
3. 深入理解內(nèi)部類
1. 為什么成員內(nèi)部類可以無(wú)條件訪問(wèn)外部類的成員和類屬性馏艾?
成員內(nèi)部類可以無(wú)條件訪問(wèn)外部類的成員變量、static變量奴愉、成員函數(shù)和static函數(shù)琅摩。
public class Outter {
private static int b = 2;
private int a = 1;
protected class Inner {
public Inner() {
System.out.println(a);//成員變量
System.out.println(b);//static變量
print();//成員函數(shù)
staticPrint();//static函數(shù)
}
}
private void print() {
}
private static void staticPrint() {
}
}
我們通過(guò)反編譯字節(jié)碼文件看看究竟是如何實(shí)現(xiàn)的。在編譯時(shí)锭硼,會(huì)將內(nèi)部類單獨(dú)編譯成一個(gè)字節(jié)碼文件房资。
Outter.class是外部類的字節(jié)碼文件,Outter$Inner.class才是成員內(nèi)部類的字節(jié)碼文件檀头。
反編譯Outter$Inner.class文件
javap -v Outter$Inner.class
得到一個(gè)關(guān)鍵信息
final cn.lastwhisper.javabasic.InnerClass.member.Outter this$0;
這是一個(gè)指向外部類對(duì)象的指針轰异。也就是說(shuō)編譯器會(huì)默認(rèn)為成員內(nèi)部類添加了一個(gè)指向外部類對(duì)象的引用岖沛,那么這個(gè)引用是如何賦初值的呢?下面接著看內(nèi)部類的構(gòu)造器:
public cn.lastwhisper.javabasic.InnerClass.member.Outter$Inner(cn.lastwhisper.javabasic.InnerClass.member.Outter);
從這里可以看出搭独,雖然我們?cè)诙x的內(nèi)部類的構(gòu)造器是無(wú)參構(gòu)造器婴削,編譯器還是會(huì)默認(rèn)添加一個(gè)參數(shù),該參數(shù)的類型為指向外部類對(duì)象的一個(gè)引用牙肝,所以成員內(nèi)部類中的Outter this&0 指針便指向了外部類對(duì)象唉俗,因此可以在成員內(nèi)部類中隨意訪問(wèn)外部類的成員。從這里也間接說(shuō)明了成員內(nèi)部類是依賴于外部類的配椭,如果沒(méi)有創(chuàng)建外部類的對(duì)象虫溜,則無(wú)法對(duì)Outter this&0引用進(jìn)行初始化賦值,也就無(wú)法創(chuàng)建成員內(nèi)部類的對(duì)象了股缸。
2. 為什么靜態(tài)內(nèi)部類只能訪問(wèn)外部類的成員屬性吼渡?
靜態(tài)內(nèi)部類只能訪問(wèn)外部類的static變量和static函數(shù)。
public class Outter {
private static int b = 2;
private int a = 1;
protected static class Inner {
public Inner() {
//System.out.println(a);//成員變量 會(huì)報(bào)錯(cuò)“Non-static field 'a' cannot be referenced from a static context”
System.out.println(b);//static變量
//print();//成員函數(shù) 會(huì)報(bào)錯(cuò)“Non-static field 'a' cannot be referenced from a static context”
staticPrint();//static函數(shù)
}
}
private void print() {
}
private static void staticPrint() {
}
}
如果靜態(tài)內(nèi)部類使用外部類的成員變量乓序,就會(huì)報(bào)錯(cuò)“Non-static field 'a' cannot be referenced from a static context”,從字面意思很好理解坎背,非static字段不能被static上下文所引用替劈。靜態(tài)內(nèi)部類使用外部類的成員函數(shù)情況也類似。
類比著上一個(gè)問(wèn)題得滤,通過(guò)反編譯字節(jié)碼文件陨献,發(fā)現(xiàn)編譯器在編譯時(shí)并沒(méi)有添加外部類的引用,所以靜態(tài)內(nèi)部類也無(wú)法使用外部類的成員變量和成員函數(shù)懂更。
public cn.lastwhisper.javabasic.InnerClass.Static.Outter$Inner();
3. 為什么匿名內(nèi)部類只能訪問(wèn)final修飾的局部變量眨业?
在JDK8以前匿名內(nèi)部類只能訪問(wèn)final修飾的局部變量,在JDK8以后匿名內(nèi)部類可以訪問(wèn)非final修飾的局部變量
想必這個(gè)問(wèn)題也曾經(jīng)困擾過(guò)很多人沮协,在討論這個(gè)問(wèn)題之前龄捡,先看下面這段代碼:
public class FinalTest {
public void test(final int b) {
final int a = 100;
new Thread() {
public void run() {
System.out.println(a);
System.out.println(b);
}
}.start();
}
}
這段代碼會(huì)被編譯成兩個(gè)class文件:FinalTest$1.class和FinalTest.class
默認(rèn)情況下,編譯器會(huì)為匿名內(nèi)部類起名為“外部類名稱+$+匿名類順序”
即test方法里面的匿名內(nèi)部類為:FinalTest$1.class
上段代碼中慷暂,如果把變量a和b前面的任一個(gè)final去掉聘殖,這段代碼都編譯不過(guò)。我們先考慮這樣一個(gè)問(wèn)題:
當(dāng)test方法執(zhí)行完畢之后行瑞,局部變量a的生命周期就結(jié)束了奸腺,而此時(shí)Thread對(duì)象的生命周期很可能還沒(méi)有結(jié)束,那么在Thread的run方法中繼續(xù)訪問(wèn)test方法的局部變量a就變成不可能了血久,但是又要實(shí)現(xiàn)這樣的效果突照,怎么辦呢?Java采用了 復(fù)制 的手段來(lái)解決這個(gè)問(wèn)題氧吐。將這段代碼的字節(jié)碼反編譯可以得到下面的內(nèi)容:
得到的信息很多讹蘑,我們分成兩個(gè)部分末盔。
第一部分run方法
我們看到在run方法中有一條指令:
bipush 100
這條指令表示將操作數(shù)100壓棧,表示使用的是一個(gè)本地局部變量衔肢。這個(gè)過(guò)程是在編譯期間由編譯器默認(rèn)進(jìn)行庄岖,如果這個(gè)變量的值在編譯期間可以確定,則編譯器默認(rèn)會(huì)在匿名內(nèi)部類的常量池中添加一個(gè)內(nèi)容相等的字面量或者直接將相應(yīng)的字節(jié)碼嵌入到執(zhí)行字節(jié)碼中角骤。
這樣一來(lái)隅忿,匿名內(nèi)部類方法中引用的變量其實(shí)并不是外部類方法中的局部變量,而是引用編譯器在匿名內(nèi)部類的常量池中添加的一個(gè)內(nèi)容相等的字面量邦尊。即匿名內(nèi)部類run方法中使用的a并不是test方法中的a背桐,而是FinalTest$1常量池中的a=100。
第二部分匿名內(nèi)部類的構(gòu)造函數(shù)
我們看到匿名內(nèi)部類FinalTest$1的構(gòu)造器含有兩個(gè)參數(shù)蝉揍,一個(gè)是指向外部類對(duì)象的引用链峭,一個(gè)是int型變量,很顯然又沾,這里是將變量test方法中的形參b以參數(shù)的形式傳進(jìn)來(lái)對(duì)匿名內(nèi)部類中的拷貝(變量b的復(fù)制)進(jìn)行賦值初始化弊仪。
也就說(shuō)如果局部變量的值在編譯期間就可以確定,則直接在匿名內(nèi)部里面創(chuàng)建一個(gè)拷貝杖刷。如果局部變量的值無(wú)法在編譯期間確定励饵,則通過(guò)構(gòu)造器傳參的方式來(lái)對(duì)拷貝進(jìn)行初始化賦值。
這樣一來(lái)就解決了前面所說(shuō)的生命周期不一致的問(wèn)題滑燃。但是新的問(wèn)題又來(lái)了役听,既然在run方法中訪問(wèn)的變量a和test方法中的變量a不是同一個(gè)變量,當(dāng)在run方法中改變變量a的值的話表窘,會(huì)出現(xiàn)什么情況典予?
對(duì),會(huì)造成數(shù)據(jù)不一致性乐严,這樣就達(dá)不到原本的意圖和要求瘤袖。為了解決這個(gè)問(wèn)題,java編譯器就限定必須將變量a限制為final變量昂验,不允許對(duì)變量a進(jìn)行更改(對(duì)于引用類型的變量孽椰,是不允許指向新的對(duì)象),這樣數(shù)據(jù)不一致性的問(wèn)題就得以解決了凛篙。
至此我們可以回答“為什么匿名內(nèi)部類只能訪問(wèn)final修飾的局部變量黍匾?”了。
1. Java為了避免數(shù)據(jù)不一致性的問(wèn)題呛梆,做出了匿名內(nèi)部類只可以訪問(wèn)final的局部變量的限制锐涯。
2. 補(bǔ)充:Java為了局部變量與匿名內(nèi)部類生命周期不一致的問(wèn)題,將匿名內(nèi)部類使用到的外部類方法局部變量復(fù)制到自己的常量池中一份填物,操作時(shí)只使用自己常量池中的數(shù)據(jù)纹腌。
4. 為什么需要內(nèi)部類
至此我們已經(jīng)看到了許多描述內(nèi)部類的語(yǔ)法和語(yǔ)義霎终,但是這并不能回答“為什么需要內(nèi)部類” 這個(gè)問(wèn)題。
一般說(shuō)來(lái)升薯,內(nèi)部類繼承自某個(gè)類或?qū)崿F(xiàn)某個(gè)接口莱褒,內(nèi)部類的代碼操作創(chuàng)建它的外圍類的對(duì)象。所以可以認(rèn)為內(nèi)部類提供了某種進(jìn)入其外圍類的窗口涎劈。
內(nèi)部類必須要回答的一個(gè)問(wèn)題是:如果只是需要一個(gè)對(duì)接口的引用广凸,為什么不通過(guò)外圍類實(shí)現(xiàn)那個(gè)接口呢?答案是:“如果這能滿足需求蛛枚,那么就應(yīng)該這樣做谅海。” 那么內(nèi)部類實(shí)現(xiàn)一個(gè)接口與外圍類實(shí)現(xiàn)這個(gè)接口有什么區(qū)別呢蹦浦?答案是:后者不是總能享用到接口帶來(lái)的方便扭吁, 有時(shí)需要用到接口的實(shí)現(xiàn)。所以盲镶,使用內(nèi)部類最吸引人的原因是 :
每個(gè)內(nèi)部類都能獨(dú)立地繼承自一個(gè)(接口的)實(shí)現(xiàn)侥袜,所以無(wú)論外圍類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對(duì)于內(nèi)部類都沒(méi)有影響溉贿。
內(nèi)部類使得多重繼承的解決方案變得完整枫吧。接口解決了部分問(wèn)題,而內(nèi)部類有效地實(shí)現(xiàn)了 “多重繼承” 顽照。 也就是說(shuō),內(nèi)部類允許繼承多個(gè)非接口類型(類或抽象類)闽寡。
實(shí)例:
使用一個(gè)類繼承兩個(gè)抽象類代兵,模擬“多繼承問(wèn)題”
abstract class MyClass1 { }
abstract class MyClass2 { }
/**
* 成員內(nèi)部類實(shí)例
* @author lastwhisper
*/
public class OuterClass extends MyClass1 {
private String name;
public OuterClass(String name) {
this.name = name;
}
// 成員內(nèi)部類,類比對(duì)象的成員變量
class InnerClass extends MyClass2 {
int innerPrice;
public InnerClass(int innerPrice) {
System.out.println("成員內(nèi)部類~類比對(duì)象的成員變量");
this.innerPrice = innerPrice;
}
public void print() {
helloInnerClass();
System.out.println("出售:" + name + " 單價(jià):" + innerPrice);
}
}
public void helloInnerClass() {
System.out.println("我是外部類的helloInnerClass方法爷狈,內(nèi)部類你可以調(diào)用我");
}
public static void main(String[] args) {
OuterClass sample = new OuterClass("香蕉");
InnerClass inner = sample.new InnerClass(20);
inner.print();
}
}
如果不需要解決“多重繼承” 的問(wèn)題植影,那么自然可以用別的方式編碼,而不需要使用內(nèi)部類涎永。但如果使用內(nèi)部類思币,還可以獲得其他一些特性:
- 內(nèi)部類可以有多個(gè)實(shí)例,每個(gè)實(shí)例都有自己的狀態(tài)信息羡微,并且與其外圍類對(duì)象的信息相互獨(dú)立谷饿。
- 在單個(gè)外圍類中,可以讓多個(gè)內(nèi)部類以不同的方式實(shí)現(xiàn)同一個(gè)接口妈倔,或繼承同一個(gè)類博投。
- 創(chuàng)建內(nèi)部類對(duì)象的時(shí)刻并不依賴于外圍類對(duì)象的創(chuàng)建。
- 內(nèi)部類并沒(méi)有令人迷惑的“is-a”關(guān)系盯蝴; 它就是一個(gè)獨(dú)立的實(shí)體 毅哗。
5. 總結(jié)
訪問(wèn)權(quán)限:
- 成員內(nèi)部類和靜態(tài)內(nèi)部類可以擁有private訪問(wèn)權(quán)限听怕、protected訪問(wèn)權(quán)限、public訪問(wèn)權(quán)限及包訪問(wèn)權(quán)限虑绵。
- 如果成員內(nèi)部類Inner用private修飾尿瞭,則只能在外部類的內(nèi)部訪問(wèn),如果用public修飾翅睛,則任何地方都能訪問(wèn)声搁;如果用protected修飾,則只能在同一個(gè)包下或者繼承外部類的情況下訪問(wèn)宏所;如果是默認(rèn)訪問(wèn)權(quán)限酥艳,則只能在同一個(gè)包下訪問(wèn)。
- 這一點(diǎn)和外部類有一點(diǎn)不一樣爬骤,外部類只能被public和包訪問(wèn)兩種權(quán)限修飾充石。我個(gè)人是這么理解的,由于成員內(nèi)部類看起來(lái)像是外部類的一個(gè)成員霞玄,所以可以像類的成員一樣擁有多種權(quán)限修飾骤铃。
資源使用:
- 成員內(nèi)部類可以無(wú)條件的使用外部類的一切靜態(tài)變量、成員變量坷剧、靜態(tài)方法惰爬、成員方法,用于內(nèi)部類和外部類通信惫企。
- 局部?jī)?nèi)部類可以無(wú)條件的使用外部類的一切靜態(tài)變量撕瞧、成員變量、靜態(tài)方法狞尔、成員方法丛版,用于內(nèi)部類和外部類通信。
- 靜態(tài)內(nèi)部類只能使用外部類的一切成員變量偏序、成員方法页畦,用于內(nèi)部類和外部類通信。
- 匿名內(nèi)部類只能使用外部類的一切成員變量研儒、成員方法豫缨,用于內(nèi)部類和外部類通信。
使用內(nèi)部類的好處:
- 可以解決“多繼承問(wèn)題”
- 內(nèi)部類可以有多個(gè)實(shí)例端朵,每個(gè)實(shí)例都有自己的狀態(tài)信息好芭,并且與其外圍類對(duì)象的信息相互獨(dú)立。
- 在單個(gè)外圍類中冲呢,可以讓多個(gè)內(nèi)部類以不同的方式實(shí)現(xiàn)同一個(gè)接口栓撞,或繼承同一個(gè)類。
- 創(chuàng)建內(nèi)部類對(duì)象的時(shí)刻并不依賴于外圍類對(duì)象的創(chuàng)建。
- 內(nèi)部類并沒(méi)有令人迷惑的“is-a”關(guān)系瓤湘; 它就是一個(gè)獨(dú)立的實(shí)體 瓢颅。
參考
《Java編程思想》
https://www.cnblogs.com/dolphin0520/p/3811445.html
https://www.cnblogs.com/cuipengfei/p/3150542.html#3901831