前言
- 上一篇文章java基礎
本篇文章繼續Java知識點的歸納,梳理一下關於面向對象的知識點,涉及到封裝、繼承、多態,還有接口,類之間的關係。
接口和抽象類
1、抽象類
抽象類和抽象方法都用abstract關鍵字進行聲明,抽象類不能被實例化,不能直接創建,抽象方法必須放在抽象類中,類中如果有抽象方法,這個類繼續聲明爲抽象類,如下:
public abstract class Hero{
public abstract void fight();
}
//子類實現抽象方法,完成實際操作
public class Warrior extends Hero{
@Override
public void fight(){}
}
//子類繼續聲明爲抽象類
public abstract class LongRange extends Hero{}
2、接口
接口被認爲是一種特殊的抽象類,同樣不能使用new實例化,接口的字段和方法都默認爲public,且不能聲明爲private或protected,接口只能包含常量(static final)和待實現的方法,java8以後接口中可以有方法的實現,如下:
interface Eat{
//...
default public void eating(){
System.out.println("eating");
}
}
在接口中,也可以定義內部類,但是隻能是public static修飾的內部類,所以接口中只能定義靜態內部類,但是在接口中定義內部類的用處不大,很少使用。
3、接口和抽象類的比較
變量 | 成員方法 | 構造方法 | 使用場合 | |
---|---|---|---|---|
抽象類 | 無限制 | 無限制 | 可以有 | 強的“is a”關係(是一種) |
接口 | 所有變量必須是public static final | 所有方法必須是public abstract | 無 | 弱的“is a”關係(is kind of,是一類) |
在很多情況下,接口優先於抽象類。因爲接口沒有抽象類嚴格的類層次結構要求,可以靈活地爲一個類添加行爲。並且從 Java 8 開始,接口也可以有默認的方法實現,使得修改接口的成本也變的很低,而且接口可以實現多繼承。
外部類和內部類
把一個類放在另外一個類的內部,處於類內部的類就叫做內部類,包含內部類的類就叫做外部類,一個外部類不一定有內部類,但是一個內部類就一定有一個”依附“的外部類,內部類比外部類多使用3個修飾符:private、protected、static,外部類不能使用這3個修飾符,雖然外部類和內部類整體是一個類文件,但是編譯之後,它會分別生成外部類的class文件和內部類的class文件,這說明在運行時外部類和內部類其實是兩個獨立的類,內部類可以分爲:非靜態內部類、靜態內部類、局部內部類、匿名內部類。
1、非靜態內部類
public class OuterClass {
private String outer = "外部類私有變量";
private void outerMethod(){
System.out.println("外部類私有方法" );
}
public class InnerClass{
public void innerMethod(){
//非靜態內部類調用外部類的私有方法
outerMethod();
//非靜態內部類訪問外部類的私有變量
System.out.println(outer);
}
}
}
沒有使用static修飾的就是非靜態內部類,它依賴於外部類實例,要創建非靜態內部類實例,必須先創建外部類實例,如下:
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
//通過 外部類實例 調用非靜態內部類的構造器
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.innerMethod();
}
總的來說,非靜態內部類有以下幾個特點:
- 1、非靜態內部類屬於外部類實例,必須依賴外部類實例才能夠創建;
- 2、非靜態內部類編譯之後,會保留一個外部類對象的引用(在構造方法中傳入);
- 3、非靜態內部類不能含有靜態成員、靜態方法和static語句塊;
- 4、非靜態內部類中可以直接訪問外部類的所有成員和方法(包括private的),但是在外部類中不能直接訪問內部類的任何成員和方法,必須先創建實例後才能訪問(包括private的)。
如果外部類和內部類的成員變量重名,可以通過this和外部類.this區分。
2、靜態內部類
public class OuterClass {
private static String outer = "外部類私有變量";
private static void outerMethod(){
System.out.println("外部類私有方法" );
}
public static class InnerClass{
public void innerMethod(){
//靜態內部類調用外部類的私有靜態方法
outerMethod();
//靜態內部類訪問外部類的私有靜態變量
System.out.println(outer);
}
}
}
使用static修飾的就是靜態內部類,它依賴於外部類,創建靜態內部類實例時,不需要先創建外部類實例,因爲靜態內部類依賴的是類而不是類實例,如下:
public static void main(String[] args) {
//通過 外部類 調用靜態內部類的構造器
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.innerMethod();
}
總的來說,靜態內部類有以下幾個特點:
- 1、靜態內部類屬於類,而不屬於類實例;
- 2、靜態內部類編譯之後,不會含有外部類對象的引用;
- 3、靜態內部類可以包含靜態成員、靜態方法和static語句塊,也能包含非靜態的;
- 4、靜態內部類不能直接訪問外部類的實例成員或方法,只能訪問靜態成員或方法(包括private的),外部類可以直接通過類名訪問靜態內部類的靜態成員或方法(包括private的),如果要訪問實例成員或方法,必須先創建實例後才能訪問(包括private的)。
外部類和內部類之間要訪問對方的private/protected成員時,編譯器會自動生成合適的“access method”靜態方法來提供合適的可訪問性,這樣就繞開了原本的成員的可訪問性不足的問題。
3、局部內部類
public class OuterClass {
public static void main(String[] args) {
//局部內部類
class InnerClass{
private String inner = "內部類私有變量";
private void innerMethod(){
System.out.println("內部類私有方法");
}
}
InnerClass innerClass = new InnerClass();
//訪問局部內部類的私有變量
System.out.println(innerClass.inner);
//調用局部內部類的私有方法
innerClass.innerMethod();
}
}
把一個類在方法裏定義,這個類就是局部內部類,局部內部類只能在方法裏面使用,它的作用域在方法之內,由於局部內部類不能在方法之外使用,所以它不能使用static和任何訪問修飾符修飾,局部內部類在開發中很少用到,因爲它的作用域太小了,不能被其他方法複用。
4、匿名內部類
public class OuterClass {
private void outerMethod(){
//創建匿名內部類
InnerClass innerClass = new InnerClass(){
@Override
public void innerMethod() {
System.out.println("內部類私有方法");
}
};
//調用匿名內部類的方法
innerClass.innerMethod();
}
public abstract class InnerClass{
int num = 1;
abstract void innerMethod();
}
}
匿名內部類就是沒有名字的內部類,它沒有使用class關鍵字來定義類,而是在使用時直接創建接口或抽象父類的實現類來使用,匿名內部類一般在只使用一次的場景下使用,總的來說,匿名內部類有以下幾個特點:
- 1、匿名內部類不能繼續聲明爲抽象類,它必須實現接口或抽象父類的所有抽象方法;
- 2、匿名內部類不能定義構造器,因爲它沒有名字,但是它可以使用實例語句塊來完成初始化;
- 3、匿名內部類必須實現一個接口或抽象父類,但最多隻能實現一個接口或抽象父類;
- 4、匿名內部類編譯之後,會保留一個外部類對象的引用。
在java8之前,局部內部類或匿名內部類訪問方法的局部變量時,這個局部變量必須使用final修飾,在java8之後,被局部內部類或匿名內部類訪問的局部變量編譯器會自動加上final,無需顯式聲明,但是如果局部變量在內部類中被修改,那麼它還是要顯式聲明final,總之,在局部內部類或匿名內部類中使用局部變量時,必須按照有final修飾的方式來用(一次賦值,以後不能重複賦值)。
面向對象三大特性
面向對象是一種程序設計方法,它的基本思想是封裝、繼承、多態,根據現實世界中各種事物的本質特點,把它們抽象成類,作爲系統的基本構成單元,然後這些類可以生成系統中的多個對象,而這些對象則是映射成客觀世界的各種事物的實例。
1、封裝
封裝就是儘可能地隱藏對象內部的實現細節,只保留一些對外接口使之與外部發生聯繫。用戶無需知道對象內部的細節,但可以通過對象對外提供的接口來訪問該對象。
2、繼承
繼承是面向對象實現複用的手段,繼承是一種“is a”關係,父類和子類之間必須存在“is a”關係,通過繼承,子類可以獲得父類的所有非 private 屬性和方法,父類的私有屬性在子類中不能直接訪問,例如Cat 和 Animal 就是一種 “is a” 關係,因此 Cat 可以繼承自 Animal,從而獲得 Animal 非 private 的屬性和方法。
2.1、父類構造和子類構造
- 構造方法不可繼承,使用super關鍵字調用父類構造
- 默認會先調用父類構造,再調用子類構造
2.2、子類調用父類信息
- 使用super關鍵字
- 可以調用父類的公有屬性和方法
- 可以調用父類的protected屬性和方法
下面一張表給出java中訪問權限修飾符的訪問範圍:
修飾符 | 在同一類中可訪問 | 在同一包內可訪問 | 在子類內可訪問 | 在不同包可訪問 |
---|---|---|---|---|
public | 可以 | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 可以 | – |
default | 可以 | 可以 | – | – |
private | 可以 | – | – | – |
2.3、方法重寫
在子類中提供一個對方法的新的實現。
- 方法重寫發生在通過繼承而相關的不同類中
- 方法重寫具有相同的方法簽名和返回值
- 子類重寫方法時子類方法訪問權限大於父類的
- 子類重寫方法時子類拋出的異常類型是父類拋出異常的子類。
- @Overiide稱爲重寫標註,用來保證重寫的方法和原方法的簽名和返回值一致
方法重載:方法重載是指在於同一個類中,一個方法與已經存在的方法名稱上相同,但是參數類型、個數、順序至少有一個不同。應該注意的是,返回值不同,其它都相同不算是重載。
3、多態
多態是指子類對象可以直接賦值給父類變量,但是運行時依然表現出子類的行爲特徵,這表示同一個類型的對象在運行時可以具有多種形態,多態分爲以下兩種:
- 編譯時多態:主要指方法的重載
- 運行時多態:指程序中定義的對象引用所指向的具體類型在運行期間才確定(運行時多態有三個條件:繼承,方法重寫,向上轉型)
例如下面的代碼中,子類Warrior繼承父類Hero,它重寫了父類的fight方法,並且在main函數中父類Hero引用子類Warrior(父類引用指向子類對象稱爲向上轉型),在Hero引用調用fight方法時,會執行實際對象所在類的fight方法,即Warrior類的fight方法,而不是Hero的fight方法。
public class Hero{
public void fight(){
System.out.println("hero");
}
}
public class Warrior extends Hero{
@Override
public void fight(){
System.out.println("Warrior");
}
}
public static void main(String[] args) {
Hero warrior = new Warrior();
warrior.fight();
}
輸出:Warrior
類圖
瞭解下面6種關係有助於看懂UML圖。
1、泛化關係(Generalization)
泛化關係用一條帶空心箭頭的直線表示,在類圖中表示爲類的繼承關係(“is a”關係),在java中用extends關鍵字表示,最終代碼中,泛化關係表現爲繼承非抽象類。例如下面ASUS繼承自Laptop,ASUS是一臺筆記本,ASUS與Laptop之間是泛化關係。
2、實現關係(Realization)
實現關係用一條帶空心箭頭的虛線表示,在類圖中表示實現了一個接口(在java中用implements 關鍵字表示),或繼承抽象類,實現了抽象類中的方法。例如下面Laptop實現了IO接口,同時它繼承Computer這個抽象類,Laptop是它們的具體實現。
3、聚合關係(Aggregation)
聚合關係用一條帶空心菱形箭頭的直線表示,表示整體是由部分組成的,但是整體和部分之間並不是強依賴的,整體不存在了,部分還是會存在。例如下面表示Staff聚合到Department,或者說部門是由員工組成的,部門不存在了,員工還是會存在的。
4、組合關係 ( Composition )
組合關係是用一條帶實心菱形箭頭的直線表示,和聚合關係不同,組合關係中整體和部分是強依賴的,即整體不存在了部分也不存在,組合關係是一種強依賴的特殊聚合關係。例如下面表示Department組合到Company中,或者說Company是由Department組成的,但是公司不存在了,部門也將不存在。
5、關聯關係(Association)
關聯關係用一條直線表示,表示不同類對象之間有關聯,這是一種靜態關係,與運行過程的狀態無關,在最開始就可以確定。因此也可以用 1 對 1、多對 1、多對多這種關聯關係來表示.。例如下面學生和學校就是一種關聯關係,一個學校可以有很多學生,但是一個學生只屬於一個學校,因此這是一種多對一的關係,在運行開始之前就可以確定。
關聯關係默認不強調方向,表示對象之間相互知道,如果要特別強調方向,如下圖,表示A知道B,但是B不知道A,這又叫DirectedAssociation。
ps: 在最終代碼中,關聯對象通常以成員變量的形式存在。
6、依賴關係(Dependency)
依賴關係用一條帶箭頭的虛線表示,與關聯關係不同的是,他描述一個對象在運行期間會用到另一個對象的關係,是一種動態關係,並且隨着運行時的變化, 依賴關係也可能發生變化。依賴也有方向,但是我們總是應該保持單向依賴,避免雙向依賴的產生。例如下面表示A依賴於B,A的一個方法中使用到了B作爲參數。
ps: 在最終代碼中,依賴關係主要表現爲:
1、A 類是 B 類方法的局部變量;
2、A 類是 B 類方法或構造的傳入參數;
3、A 類向 B 類發送消息,從而影響 B 類發生變化;
箭頭的指向爲調用關係。
結語
本文都是關於面向對象的一些知識,雖然簡單,但是也挺繁瑣的,積少成多,希望大家閱讀過後有所收穫。
參考資料: