#############java面向對象詳解#############
1、面向對象基本概念
2、類與對象
3、類和對象的定義格式
4、對象與內存分析
5、封裝性
6、構造方法
7、this關鍵字
8、值傳遞與引用傳遞?
9、對象的一對一關系
10、static關鍵字
11、main方法分析
12、繼承
13、對象的初始化
14、方法的重寫
15、super關鍵字
16、final關鍵字
17、抽象類
18、接口
19、多態性
20、instanceof關鍵字
21、內部類
######################################
1、面向對象基本概念
其本質是以建立模型體現出來的抽象思維過程和面向對象的方法(百度百科)
是一種編程思維,也是一種思考問題的方式
如何建立面向對象的思維呢?
1、先整體,再局部
2、先抽象,再具體
3、能做什么,再怎么做
2、類與對象
類:類是一種分類,一個類別,一個模板,它描述一類對象的行為和狀態,是一組具有相同特性(屬性)與行為(方法)的事物集合
對象:是一個個性的產物,是一個個體的特征,是類的一個實例,有狀態和行為
3、類和對象的定義格式
類的定義:
class 類名稱{
屬性名稱;
返回值類型 方法名稱(){}
}
對象的定義:
一個類要想真正的進行操作,則必須依靠對象,對象的定義格式如下:
類名稱 對象名稱 = new 類名稱() ;
如果要想訪問類中的屬性或方法(方法的定義),則可以依靠以下的語法形式:
訪問類中的屬性:對象.屬性 ;
調用類中的方法:對象.方法();
在java中對象聲明有兩種含義
聲明對象:Horse horse= null; ; //表示聲明了一個對象,但是此對象無法使用,horse沒有具體的內存指向
實例化對象:horse= new Horse() ;// 表示實例化了對象,可以使用
horse.eat(); //通過對象調用方法
new Horse().eat();//匿名對象調用方法
4、對象與內存分析
new 關鍵字表示創建一個對象
new 關鍵字表示實例化對象
new 關鍵字表示申請內存空間
注意:如果使用一個沒有申請內存空間的對象,會報空指針異常:java.lang.NullPointerException
(1)new關鍵字:表示向內存申請空間,也表示實例化一個對象,創建一個對象。
(2)一個對象在內存中的大小,由該對象的所有屬性所占的內存大小的總和。引用類型變量在32位系統上占4個字節,在64位系統上占8個字節。加上而外的對象隱性數據所占的大小。
(3)相同的類型才可以賦值
(4)不同的引用,指向同一個對象,任何一個引用改變對象的值,其它引用都會反映出來。
(5)編程時要注意的問題,在確定不使用對象時,要盡早釋放對象:引用=null
(6)當一個堆中的對象沒有被任何引用變量所指向時,該對象會被JVM 的 GC 程序認為是垃圾對象,從而被回收
5、封裝性
封裝性的概念
封裝性是面向對象思想的三大特征之一,封裝就是隱藏實現細節,僅對外提供訪問接口。實現細節部份包裝、隱藏起來的方法。
封裝有:屬性的封裝、方法的封裝、類的封裝、組件的封裝、模塊化封裝、系統級封裝…
封裝的好處:模塊化、信息隱藏、代碼重用、插件化易於調試、具有安全性
封裝的缺點:會影響執行效率
封裝之前:屬性都可以直接訪問和修改
class Person{
String name;
int age;
}
封裝之后:
class Person{
//屬性是成員變量,私有化屬性,使得其他對象不能直接訪問屬性
private String name;
private int age;
//參數及方法內定義的變量是局部變量
public void setName(String name){
this.name = name;}
public String getName(){
return name;}
}
成員變量和局部變量的區別
a、在類中的位置不同
成員變量:在類中定義
局部變量:在方法中定義或者方法的參數
b、在內存中的位置不同
成員變量:在堆內存(成員變量屬於對象,對象進堆內存)
局部變量:在棧內存(局部變量屬於方法,方法進棧內存)
c、生命周期不同
成員變量:隨着對象的創建而存在,隨着對象的銷毀而消失
局部變量:隨着方法的調用而存在,隨着方法的調用完畢而消失
d、初始化值不同
成員變量:有默認初始化值,引用類型默認為null
局部變量:沒有默認初始化值,必須定義,賦值,然后才能使用
注意:
局部變量名稱可以和成員變量名稱一樣,在方法中使用的時候,采用的是就近原則。
6、構造方法
無參構造方法:
public Dog(){} //如果一個類沒有定義構造方法,則默認無無參構造,如果有定義有參構造,最好再顯示定義一個無參構造方法
帶參構造方法:
public Dog(String name){
this.name = name;
}
多參構造方法:
public Dog(String name,int age){
this.name = name;
this.age = age;
}
(1)構造方法名稱與類名相同,沒有返回值聲明(包括 void)
(2)構造方法用於初始化數據(屬性)
(3)每一個類中都會有一個默認的無參的構造方法
(4)如果類中有顯示的構造方法,那么默認構造方法將無效
(5)如果有顯示的構造方法,還想保留默認構造 方法,需要顯示的寫出來。
(6)構造方法可以有多個,但參數不一樣,稱為構造方法的重載
(7)在構造方法中調用另一個構造方法,使用this(...),該句代碼必須在第一句。
(8)構造方法之間的調用,必須要有出口。
(9)給對象初始化數據可以使用構造方法或setter方法,通常情況下,兩者都會保留。
(10)一個好的編程習慣是要保留默認的構造方法。(為了方便一些框架代碼使用反射來創建對象)
(11)private Dog(){},構造方法私有化,當我們的需求是為了 保正該類只有一個對象時(單例模式就是私有化構造器)。
7、this關鍵字
this關鍵字指向的是當前對象的引用
調用類中的屬性:this.屬性名稱,指的是訪問類中的成員變量,用來區分成員變量和局部變量(重名問題)
調用類中的方法:this.方法名稱,用來訪問本類的成員方法
調用類構造方法:this();訪問本類的構造方法,()中可以有參數的 如果有參數 就是調用指定的有參構造
注意:
1.this() 不能使用在普通方法中,只能寫在構造方法中
2.必須是構造方法中的第一條語句
8、值傳遞與引用傳遞?
首先,注意:在java中只有按值傳遞,並沒有所謂的按引用傳遞
java數據類型可以分為兩大類:基本類型(primitive types)和引用類型(reference types)
a、基本數據類型的按值傳遞
public class Swap { |
main函數調用swap函數來交換 x,y的值,然而調用函數之后發現main中x,y的值並未交換。包括在Java api中找不到一個可以交換兩個變量的方法。這與Java語言的特性有關。通過一個圖就可以知道上面程序的運行結果了。
main函數中的x,y和swap函數中的x,y分別存放在不同的區域,在main中調用swap函數的時候,會將main中的x,y的值賦給swap中的x,y。當swap函數中對x,y交換時只是對swap幀中的x,y做交換,並不會改變main中的x,y。所以當函數返回時main中的x,y並不會改變
b、引用數據類型的按值傳遞
引用數據數據類型分為三種:①接口 ②類 ③數組
public static void main(String[] args) { |
運行程序后發現,swap函數對a[0] ,a[1]的操作竟然影響到了main函數中的a[0] ,a[1]的值,真是不可思議。為什么會產生如此之結果。原來引用類型的按值傳遞,傳遞的是對象的地址
由圖可以看出在swap中僅僅是得到了數組的地址,並沒有對數組的元素進行復制,在swap中對數組的操作是直接對main函數中數組的操作,因此swap函數返回后main函數中的a[0] ,a[1]的值發生交換
引用:https://blog.csdn.net/u013309870/article/details/75499175
9、對象的一對一關系
java中對象的對應關系有很多種,比如單向一對一,雙向一對一,一對多,多對一,多對多等,其實現原理相同
其實可以理解為類的組合問題,把對象當做另一個的屬性來操作,使得其產生對應關系(很多設計模式就是通過類的組合實現的)
分別是Hero、Weapon、One2One(包含主函數) class Weapon { public class One2One { hero.setAge(350); //修改其屬性 } |
引用:https://blog.csdn.net/IT_arookie/article/details/83350946
10、static關鍵字
static關鍵字的作用:方便在沒有創建對象的情況下來進行調用(方法/變量)。
a、使用static關鍵字修飾一個屬性:聲明為static的變量實質上就是全局變量
b、使用static關鍵字修飾一個方法:在一個類中定義一個方法為static,那就是說,無需本類的對象即可調用此方法(類調用)
c、使用static關鍵字修飾一個類(內部類):
聲明為static的方法有以下幾條限制:
它們僅能調用其他的static 方法,反過來是可以的。
它們只能訪問static數據。
它們不能以任何方式引用this或super。
不允許用來修飾局部變量
可以參考:https://www.cnblogs.com/dolphin0520/p/3799052.html
11、main方法分析
主方法:
public static void main(String[] args){
//代碼塊
}
public:公有的,最大的訪問權限
static:靜態的,無需創建對象
void::表示沒有返回值,無需向JVM返回結果
main:方法名,固定的方法名
String[] args:表示參數為字符串數組,可以在調用方法時傳入參數
12、繼承
繼承是面向對象三大特征之一
繼承是使用已存在的類的定義作為基礎建立新類的技術,新類的定義可以增加新的數據或新的功能,也可以用父類的功能,但不能選擇性地繼承父類
被繼承的類稱為父類(超類),繼承父類的類稱為子類(派生類)
通過繼承可以實現代碼重用
子類擁有父類非 private 的屬性、方法。
子類可以擁有自己的屬性和方法,即子類可以對父類進行擴展。
子類可以用自己的方式實現父類的方法。
構造器而言,它只能夠被調用,而不能被繼承,可以通過使用super()進行調用,
對於繼承而已,子類會默認調用父類的構造器,但是如果沒有默認的父類構造器,子類必須要顯示的指定父類的構造器(通過super()),而且必須是在子類構造器中做的第一件事(第一行代碼)。
對於protected而言,它指明就類用戶而言,他是private,但是對於任何繼承與此類的子類而言或者其他任何位於同一個包的類而言,他卻是可以訪問的
Java 的繼承是單繼承,但是可以多重繼承,
語法:[訪問權限] class 子類名 extends 父類名{
類體定義;
}
示例:
public class Dog{
private String name;
private String sex;
public void eat(){System.out.println(“吃飯”);}
}
public class HomeDog extends Dog{
//類的定義
}
public class HuskyDog extends Dog{
//類的定義
}
protected(受保護的訪問權限修飾符,用於修飾屬性和方法,使用protected修飾的屬性和方法可以被子類繼承)
(1)繼承是發生在多個類之間
(2)繼承使用關鍵字extends
(3)JAVA只能單繼承,允許多層繼承
(4)被繼承的類叫父類(超類),繼承父類的類叫子類(派生類)
(5)在父類中的非私有屬性和方法可以被子類繼承
(6)protected(受保護的訪問權限修飾符),修飾的屬性或方法可以被子類繼承
(7)構造方法不能被繼承
(8)創建對象會調用構造方法,調用構造方法不一定就是創建對象
(9)實例化子類對象,會先調用父類的構造方法,如果父類中沒有默認的構造方法,那么子類必須顯示的通過super(...)來調用父類的帶參構造方法,super也只能在子類構造方法中的第一句
繼承的好處:
a、提高代碼的復用性
b、提高代碼的維護性
c、讓類與類之間產生關系,是多態的前提
繼承的缺點:增強了類與類之間的耦合性
13、對象初始化
public class CodeBlockTest {
|
通過每執行一個代碼塊或構造函數,輸出字段在上一代碼塊執行后的值,以此來探究對象的初始化順序。
由目前的輸出結果可知,對於對象的初始化順序,我們可以得出以下結論:
1. 父類靜態字段初始化
2. 父類靜態代碼塊、子類靜態字段初始化 (接下來探究兩者的順序)
3. 子類靜態代碼塊
4. 父類普通字段初始化
5. 父類構造代碼塊
6. 父類構造函數
7. 子類普通字段初始化
8. 子類構造代碼塊
9. 子類構造函數
public class CodeBloacTest2 {
|
我們在子類靜態字段childStr初始化的時候,賦的是父類靜態字段fatherStr的值。由輸出結果可知,childStr初始化后的值是父類靜態代碼塊執行后賦予fatherStr的值。由此可知兩者的執行順序為:父類靜態代碼塊==>子類靜態字段初始化
初始化順序(最終結果):
父類靜態字段初始化
父類靜態代碼塊
子類靜態字段初始化
子類靜態代碼塊
父類普通字段初始化
父類構造代碼塊
父類構造函數
子類普通字段初始化
子類構造代碼塊
子類構造函數
引用:https://www.jb51.net/article/111157.htm
14、方法的重寫
方法重寫(overriding method)
在Java中,子類可繼承父類中的方法,而不需要重新編寫相同的方法。但有時子類並不想原封不動地繼承父類的方法,而是想做一定的修改,這就需要采用方法的重寫。方法重寫又稱方法覆蓋。
在子類和父類中,重寫方法后,在調用時,以創建的對象類型為准,會調用誰的方法。
重寫特性:
a、發生在子父類中,方法重寫的兩個方法返回值、方法名、參數列表必須完全一致(子類重寫父類的方法)
b、子類拋出的異常不能超過父類相應方法拋出的異常(子類異常不能大於父類異常)
c、子類方法的訪問級別不能低於父類相應方法的訪問級別(子類訪問級別不能低於父類訪問級別)
d、父類中的方法若使用private、static、final任意修飾符修飾,那么,不能被子類重寫。
面試題 :overloading與overriding的區別?
15、super關鍵字
可以理解為對父類的引用,使用super來調用父類的屬性,方法,和構造方法
super可以完成以下的操作:
a、使用super調用父類中的屬性,可以從父類實例處獲得信息。
b、使用super調用父類中的方法,可以委托父類對象幫助完成某件事情。
c、使用super調用父類中的構造方法(super(實參)形式),必須在子類構造方法的第一條語句,調用父類中相應的構造方法,若不顯示的寫出來,默認調用父類的無參構造方法,比如:super();
16、final關鍵字
使用final關鍵字完成以下的操作:
a、使用final關鍵字聲明一個常量
當final修飾一個基本數據類型時,表示該基本數據類型的值一旦在初始化后便不能發生變化;如果final修飾一個引用類型時,則在對其初始化之后便不能再讓其指向其他對象了,但該引用所指向的對象的內容是可以發生變化的
b、使用final關鍵字聲明一個方法
該方法為最終方法,且只能被子類繼承,但是不能被子類重寫。
c、使用final關鍵字聲明一個類
該類就轉變為最終類,沒有子類的類,fianl修飾的類無法被繼承。
d、在方法參數中使用final,在該方法內部不能修改參數的值(在內部類中詳解)
當final變量是基本數據類型以及String類型時,如果在編譯期間能知道它的確切值,則編譯器會把它當做編譯期常量使用,不過要注意,只有在編譯期間能確切知道final變量值的情況下,編譯器才會進行這樣的優化
引用變量被final修飾之后,雖然不能再指向其他對象,但是它指向的對象的內容是可變的。
可以參考:https://www.cnblogs.com/xiaoxi/p/6392154.html
17、抽象類
抽象類的基本概念
(1)很多具有相同特征和行為的對象可以抽象為一個類;很多具有相同特征和行為的類可以抽象為一個抽象類。
(2)使用abstract關鍵字聲明的類為抽象類
定義一個抽象類
abstract class Animal{
public abstract void move();
}
abstract class Person extends Animal{
private String name;
//...
public abstract void eat();//抽象方法
}
抽象類的規則:
a、抽象類可以沒有抽象方法,有抽象方法的類必須是抽象類
b、非抽象類繼承抽象類必須實現所有抽象方法
c、抽象類可以繼承抽象類,可以不實現父類抽象方法。
d、抽象類可以有方法實現和屬性
e、抽象類不能被實例化
f、抽象類不能聲明為final
g、抽象類可以有構造方法
18、接口
接口的定義格式:
interface 接口名稱{
全局常量 ;
抽象方法 ;
}
示列:
interface IEat{
//public abstract void eat();
void eat(); //默認為public abstract void eat();
//public static final int NUM = 10;
int NUM = 10; }
interface ISleep extends IEat{
void sleep();
}
接口的使用規則:
(1)定義一個接口,使用interface關鍵字
(2)在一個接口中,只能定義常量、抽象方法,JDK1.8后可以定義默認的實現方法
(3)接口可以繼承多個接口:extends xxx,xxx
(4)一個具體類實現接口使用implements關鍵字
(5)一個類可以實現多個接口
(6)抽象類實現接口可以不實現接口的方法
(7)在接口中定義的方法沒有聲明 訪問修飾符,默認為public
(8)接口不能有構造方法
(9)接口不能被實例化
java8新增
(1)增加了default方法和static方法,這兩種方法完全可以有方法體
(2)default方法屬於實例,static方法屬於類(接口)
(3)接口中的靜態方法不會被繼承,接口中的靜態變量會被繼承
public interface IUser {
static void say() {
System.out.println("say_" + IUser.class);
}
default void eat() {
System.out.println("eat_" + IUser.class);
}
}
19、多態性
多態就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性
多態性我們大概可以分為兩類:
(1)方法的重載與重寫
(2)對象的多態性
//用父類的引用指向子類對象(用大的類型去接受小的類型,向上轉型、自動轉換)
Chicken home = new HomeChicken();
在編程時針對抽象類型的編寫代碼,稱為面向抽象編程(或面向接口編程)父類通常都定義為抽象類、接口
對象的多態性:
對象多態性是從繼承關系中的多個類而來,
向上轉型:將子類實例轉為父類引用
格式:父類 父類對象 = 子類實例 ; 自動轉換
以基本數據類型操作為例:int i = ‘a' ;
(因為char的容量比int小,所以可以自動完成)
向下轉型:將父類實例轉為子類實例
格式:子類 子類對象 = (子類)父類實例 ;強制轉換
以基本數據類型操作為例:char c = (char)97;
因為整型是4個字節比char 2個字節要大,所以需要強制完成
a、方法的重載與重寫就是方法的多態性表現
b、多個子類就是父類中的多種形態
c、父類引用可以指向子類對象,自動轉換
d、子類對象指向父類引用需要強制轉換(注意:類型不對會報異常)
e、在實際開發中盡量使用父類引用(更利於擴展)
指向子類的父類引用由於向上轉型了,它只能訪問父類中擁有的方法和屬性,而對於子類中存在而父類中不存在的方法,
該引用是不能使用的,盡管是重載該方法。若子類重寫了父類中的某些方法,在調用該些方法的時候,
必定是使用子類中定義的這些方法(動態連接、動態調用)。
例如:
class A{
void fun1(){}
void fun2(){}
}
class B extends A{
void fun1(String a){} //重載fun1
void fun2(){} //重寫fun2
}
class C{
public static void main(String[] args){
A a = new B();
a.fun1(); //這里會調用A類的fun1方法,由於向上轉型,B的fun1(String a) 會被丟棄
a.fun2(); //這里調用B的fun2方法,由於是new 的B對象,而B重寫了fun2,所以會調用B的fun2
}
}
對於面向對象而言,多態分為編譯時多態和運行時多態。其中編輯時多態是靜態的,主要是指方法的重載,而運行時多態是動態的,它是通過動態綁定來實現的,也就是我們所說的多態性
java實現多態有三個必要條件:繼承、重寫、向上轉型。
繼承:在多態中必須存在有繼承關系的子類和父類。
重寫:子類對父類中某些方法進行重新定義,在調用這些方法時就會調用子類的方法。
向上轉型:在多態中需要將子類的引用賦給父類對象,只有這樣該引用才能夠具備技能調用父類的方法和子類的方法
基於繼承實現的多態
繼承是通過重寫父類的同一方法的幾個不同子類來體現的
對於引用子類的父類類型,在處理該引用時,它適用於繼承該父類的所有子類,子類對象的不同,對方法的實現也就不同,
執行相同動作產生的行為也就不同。
基於接口實現的多態
指向接口的引用必須是指定這實現了該接口的一個類的實例程序,在運行時,根據對象引用的實際類型來執行對應的方法。
可以參考:https://www.cnblogs.com/chenssy/p/3372798.html
20、instanceof關鍵字
instanceof 是用於檢查對象是否為指定的類型,通常在把父類引用強制轉換為子類引用時要使用,以避免發生類型轉換異常(ClassCastException)。
語法格式如下:
對象 instanceof 類型--返回boolean類型值
示例:
if(homeChicken instanceof Chicken){
//...
}
該語句一般用於判斷一個對象是否為某個類的實例,是返回true,否返回false
父類的設計法則
通過instanceof關鍵字,我們可以很方便的檢查對象的類型,但如果一個父類的子類過多,這樣的判斷還是顯得很繁瑣,那么如何去設計一個父類呢?
a、父類通常情況下都設計為抽象類或接口,其中優先考慮接口,如接口不能滿足才考慮抽象類。
b、一個具體的類盡可能不去繼承另一個具體類,這樣的好處是無需檢查對象是否為父類的對象。
21、內部類
內部類就是在一個類的內部定義的類。
成員內部類:內部類對象依賴外部類對象而存在,即在創建一個普通內部類對象時首先需要創建其外部類對象
內部類對象可以訪問外部類對象中所有訪問權限的字段,同時,外部類對象也可以通過內部類的對象引用來訪問內部類中定義的所有訪問權限的字段
成員內部類格式如下:
class Outer {
class Inner{}
}
編譯上述代碼會產生兩個文件:
Outer.class和Outer$Inner.class。
在外部創建內部類對象
內部類除了可以在外部類中產生實例化對象,也可以在外部類的外部來實例化。
那么,根據內部類生成的*.class文件:Outer$Inner.class
“$” 符號在程序運行時將替換成“.”
所以內部類的訪問:通過“外部類.內部類”的形式表示。
Outer out = new Outer() ;// 產生外部類實例
Outer.Inner in = null; // 聲明內部類對象
in = out.new Inner() ; // 實例化內部類對象
局部內部類
內部類可以作為一個類的成員外,還可以把類放在方法內定義(不常用,匿名內部類可以顯示局部內部類的功能)。
在局部內部類里面可以訪問外部類對象的所有訪問權限的字段,而外部類卻不能訪問局部內部類中定義的字段
注意:
a、局部內部類只能在定義該內部類的方法內實例化,不可以在此方法外對其實例化。
b、局部內部類對象不能使用該內部類所在方法的非final局部變量。
class Outer {
public void doSomething(){
class Inner{
public void seeOuter(){}
}
}
}
靜態內部類
在一個類內部定義一個靜態內部類:
靜態的含義是該內部類可以像其他靜態成員一樣,沒有外部類對象時,也能夠訪問它。靜態嵌套類僅能訪問外部類的靜態成員和方法。
靜態內部類中也無法訪問外部類的非靜態成員
class Outer{
static class Inner{}
}
class Test {
public static void main(String[] args){
Outer.Inner n = new Outer.Inner();
}
}
匿名內部類
匿名內部類就是沒有名字的內部類。
匿名內部類的三種情況:
(1)繼承式的匿名內部類
(2)接口式的匿名內部類
(3)參數式的匿名內部類
在使用匿名內部類時,要記住以下幾個原則:
(1)不能有構造方法,只能有一個實例。
(2)不能定義任何靜態成員、靜態方法。
(3)不能是public,protected,private,static。
(4)一定是在new的后面,用其隱含實現一個接口或繼承一個類。
(5)匿名內部類為局部的,所以局部內部類的所有限制都對其生效
局部內部類訪問局部變量必須用final修飾,為什么?
當調用這個方法時,局部變量如果沒有用final修飾,他的生命周期和方法的生命周期是一樣的,當方法被調用時會入棧,
方法結束后即彈棧,這個局部變量也會消失,那么如果局部內部類對象還沒有馬上消失想用這個局部變量,顯然已無法使用了,
如果用final修飾會在類加載的時候進入常量池,即使方法彈棧,常量池的常量還在,也就可以繼續使用了。
注意:在jdk1.8中取消了在局部內部類中使用的變量必須顯示的使用final修飾,編譯器默認會為這個變量加上final
內部類的作用
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外部類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。
如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,
內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。
依賴外部類對象的:成員內部類,方法內部類,匿名內部類
靜態內部類不依賴外部類的對象。所以,我們在項目中優先考慮選擇靜態內部類(不會產生內存泄露)